Kotlin: Functions(Fonksiyonlar) ve Lambdas(Lambdalar)

Yasin Ege
HardwareAndro
Published in
5 min readJul 18, 2020

Selamlar, bu yazımda Kotlin dilinde “Function” ve “Lambda” kavramlarından bahsedeceğim.

Genel Syntax

Kotlin’de fonksiyonlar “fun” anahtar sözcüğü kullanılarak bildirilir. Her dilden hatırlayacağımız bir main() fonksiyonu bulunmaktadır. Bu fonksiyon herhangi bir parametre almaz ve geri dönüş(return) değeri yoktur.

fun main() {    
println("Hello Kotlin!")
}
kotlin function hierarchy

Bir toplama fonksiyonu için aynı sonucu veren 3 farklı syntax
kullanımı

return tipi Int olan bir syntax:

fun sumSyntaxOne(x: Int, y : Int): Int {
return x+y
}

return tipi Int olan başka bir syntax:

fun sumSyntaxTwo(x: Int, y : Int): Int =  x +y

return tipi belirtilmeyen başka bir syntax kullanımı:

fun sumSyntaxThree(x: Int, y : Int) = x+y

Default Arguments

Kotlin’in bize sağladığı bir başka güzel avantaj ise fonksiyon parametrelerine default yani varsayılan bir değer tanımlayabilmemizdir. Yani çağrılan fonksiyon da belirtilen parametrelerden herhangi biri boş olarak gelirse default yani varsayılan bir değer atayarak bu değeri kendimiz belirleyebiliriz.

Bu konuyla ilgili örneği “Named Arguments” bölümünde daha detaylı anlatacağım.

Named Arguments

Bir fonksiyonu çağırırken değişkenleri isimlendirerek verebiliriz ve bu da bize daha okunabilir bir kod sunar. Ayrıca değişkenlerin varsayılan değerleri bulunuyorsa baskın değeri fonksiyona yollayabiliriz. Nasıl mı?

example of named arguments

Örneği açıklamak gerekirse, kullanıcı bilgilerini gönderdiğimiz basit bir fonksiyonumuzun olduğunu düşünelim, main() fonksiyonu içerisinde çağrılan addUser() fonksiyonuna name = “Yasin”, age = 23 değerlerini isimlendirerek gönderdik bu sayede kod okunabilirliğini büyük ölçüde arttırdık, ayrıca age değeri varsayılan olarak -1 tanımlanmasına karşın, 23 değerini gönderdik ve varsayılan değeri yok saymış olduk.

Variable number of arguments (Varargs)

Fonksiyona göndereceğimiz parametrelerin sayısı belli değil ise varargkullanabiliriz, vararg göndereceğimiz değerleri virgülle ayırarak istediğimiz sayıda değişkeni fonksiyona göndermemizi sağlar.

Bir fonksiyonda yalnızca bir vararg parametresi bulunabilir.

Spread (*) operatörü kullanarak başka bir diziyi, vararg parametresine yollayabiliriz.

Ve böylelikle aşağıdaki gibi çıktı elde ederiz:

Gonderilen parametre sayisi: 1 
Gonderilen parametre sayisi: 3
Gonderilen parametre sayisi: 1
Gonderilen parametre sayisi: 6

Örneğimizde addList adında bir fonksiyon oluşturduk. Bu fonksiyon vararg parametresini kullanıyor ve gönderdiğimiz değerleri diziye ekleyerek bize bir liste return ediyor.

Infix Notation

Infix Fonksiyonlar:

Extension (Bir sınıfa ait) fonksiyon olmak zorundadırlar.
Yalnızca bir parametre alabilirler.
Infix fonksiyonlar default değer veya vararg değişkenini kabul etmezler.

Fonksiyonun başında infix anahtar kelimesi ile tanımlanır.

infix fun Int.add(b : Int) : Int = this + bval x = 10.add(20)  // Normal kullanım
val y = 10 add 20 // infix kullanımı

Yukarıdaki fonksiyonumuzun her iki çağrılış biçimi de doğrudur . 10 add 20 veya 10.add(20) satırları çalıştığında this = 10 ve b = 20 değerini alır ve sonuç 30 olarak döndürülür.

Unit-Returning Functions

Kotlin’ de, hiçbir değer döndürmeyen fonksiyonlar için “Unit” anahtar kelimesi kullanılır. Diğer dillerde ki void anahtar sözcüğünün karşılığıdır.

Eğer biz bir fonksiyonun tipini belirlemezsek yazdığımız fonksiyon varsayılan olarak Unit’ dir. Örneğin:

fun printHello(name: String): Unit {
if (name != null)
println("Hello $name")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
////////////////////////İki farklı kullanım ///////////////////fun printHello(name: String) { ... }

Function Scope (Fonksiyon Kapsamı)

Local Functions

Kotlin dili fonksiyon içerisinde başka bir fonksiyon kullanmamızı destekler.

Aşağıdaki örnekte görüldüğü üzere dış fonksiyonun içerisinden parametreleri farklı olan başka bir iç fonksiyon çağırılabilmektedir.

fun dfs(graph: Graph) {    fun dfs(current: Vertex, visited: MutableSet<Vertex>) {        if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}

Ayrıca yerel fonksiyonlar dış fonksiyonun değişkenlerine erişim sağlayabilir.

fun dfs(graph: Graph) {    val visited = HashSet<Vertex>()    fun dfs(current: Vertex) {        if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}

Bu konuyu basit bir örnekle daha gösterelim:

Member Functions ( Üye Fonksiyonlar)

Bir sınıfın/nesnenin içerisinde yazılan fonksiyonlara “Member Function” denir.

class Message{
fun printMessage() {
print(" I'm Message... ")
}
}
fun main() {
// Message nesnesi oluşturulur ve printMessage fonksiyonu çağrılır
Message().printMessage()
}

Extension Functions

Kotlin’deki extension fonksiyonlar, yeni fonksiyonlar ekleyerek bir sınıfın işlevselliğini genişletmemize yardım eder. Bunu örnekle açıklamak gerekirse: String tipinde bir değişken oluşturduğumuzu ve bu değişkenin hemen yanına “nokta” işareti eklediğimizde dillerin bize sağladığı birçok özelliği görebiliriz:

Şimdi Kotlin dilinde kendi extension fonksiyonumuzu yazalım. Hoşuma giden bu örneği sizinle paylaşmak istiyorum. Örnekte gelen string ifadenin ilk ve son harfini silen bir fonksiyon oluşturalım.

fun String.removeFirstLastChar(): String = this.substring(1, this.length - 1)println("KOTLIN".removeLastChar())

Yukarıdaki örneği çalıştırdıktan sonra OTLI çıktısını alırız.

Yukarıdaki örnekte String.removeLastChar kısmının başında ki String ifadesi bize gelecek değerin tipinin String olacağını belirtir. Bu kısımdan sonraki : String = ifadesi fonksiyonumuzun String bir değer return edeceğini belirtir.

Extensionların kullanımı hakkında daha fazla bilgi almak isterseniz Mobil Dünyada Extension isimli yazıyı okuyabilirsiniz.

High Order Functions

Higher-order fonksiyonlar, parametre olarak fonksiyon veya fonksiyonlar alabilir ve geriye bir fonksiyon return edebilirler. Verilen örnekle daha iyi anlayacağınızı düşünüyorum.

Çıktı :         Sum ---> 5 + 9 = 14

Tail Recursive Functions

Kullandığınız herhangi bir problemde kullandığınız özyineleme fonksiyonunu düşünün(faktoriyel hesabı gibi): Son yineleme de dahil olmak üzere tüm yinelemeler bir değer döndürdükten sonra hesaplama yapıldığı için tüm değerler işlem sonuna kadar bellekte tutulur. Böyle bir yöntem belleğin verimsiz kullanılmasına sebep olur.

Kotlin tail recursion (kuyruk özyineleme) olarak bilinen fonksiyonel programlama tarzını destekler.

Bir fonksiyon tailrec anahtar kelimesi ile belirtildiğinde Kotlin bu özyineleme fonksiyonunu optimize eder ve daha hızlı hale getirir.

val eps = 1E-10 // "good enough", could be 10^-15tailrec fun findFixPoint(x: Double = 1.0): Double = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))

Lambdas

Bir lambda ifadesi her zaman kıvırcık parantezlerle çevrili olmak zorundadır{ }

Lambda ifadeleri -> işaretleyicisi kullanılarak gösterilir.

Ayrıca Kotlin bize lambda kullanarak bir önceki parametreyi sonraki parametre veya parametrelere değer olarak vermemizi sağlar. Peki bunu nasıl sağlıyor? Dilerseniz örneklerle gösterelim:

val sum = { x: Int, y: Int -> x + y }

Yukarıdaki örnekte sum değişkeni {} parantezleri ile sınırlandırılmış ve bir fonksiyona benzer yapıda kullanılmıştır.

sum(5,7,3) değerleri sırasıyla x, y ve multiply parametrelerine gönderilir, x = 3, y = 7 ve multiply = 3 değerini alır.

multiply parametresi -> işaretleyicisini kullanarak x ve y sayısını toplayan ve sonra kendisiyle çarpan bir nevi fonksiyon işlevi görmüş olur.

Sizlere Kotlin’de “Function” ve “Lambda” kavramlarını elimden geldiğince anlatmaya çalıştım. İlk Medium yazımda, bir yerleri eksik/yanlış anlatma ihtimalime karşın herkesten özür diliyor, zaman ayırıp okuduğunuz için teşekkürlerimi sunuyorum, bir başka yazıda görüşmek dileğiyle…

References :

Kotlin functions are fun

Kotlin Functions: infix, vararg, tailrec

--

--

Yasin Ege
HardwareAndro

Android Developer at Bilyoner — Flutter & Android