Kotlin — Higher-order Fonksiyonlar

Enes Okurterzi
4 min readMar 22, 2024

--

Herkese merhaba, bu yazımda higher-order fonksiyonları ve bu fonksiyonların parametre olarak aldığı fonksiyonları anlatıyor olacağım.

Higher-order fonksiyonların tanımını yaptıktan sonra parametre olarak alınan fonksiyonların nasıl oluşturulduğunu ve ardından bu fonksiyonların nasıl parametre olarak verildiklerini inceleyeceğiz. O zaman hadi başlayalım.

Higher-order fonksiyon: Fonksiyonları parametre olarak alan veya bir fonksiyon döndüren bir fonksiyondur.

Aslında tanım çok açık fakat ilk gördüğümüzde biraz düşündürüyor daha iyi anlaşılması için bir örneğe göz atacağız fakat öncesinde şöyle bir sorumuz var: Bir fonksiyon parametre olarak verilecekse bu parametrenin tipi nasıl belirtiliyor?

Fonksiyon tipi: Fonksiyonun parametre olarak alacağı değerlerin tiplerinin yazılmasından sonra “->” işaretiyle geri dönüş olarak beklenen değerin tipinin yazılmasıdır.

Artık fonksiyonlarımızın tipini nasıl belirteceğimizi de biliyoruz şimdi örneğimize bakabiliriz:

Higher-order fonksiyonlarda fonksiyon isteyen parametre sonda olduğunda, diğer parametreleri verdikten sonra parantezi kapatıp süslü parantez içinde fonksiyonu yazabilirsiniz.

Bu bilgi ile fonksiyonumuzu çağırsaydık şu şekilde görünecekti:

foo(3, "b parametresi") {
println("Merhaba")
}

Bu örnekte görmüş olduğumuz gibi a ve b değişkenlerinin tipini nasıl belirtiliyorsak c değişkeninin tipini de belirtiyoruz. Hiçbir değişken almayan ve geriye de bir değer döndürmeyen bir fonksiyon verilmesi isteniyor ve aşağıda da buna uygun şekilde bir fonksiyon yazılıyor. Peki bu fonksiyon nasıl çalışıyor?

Normal bir fonksiyonun çalışma düzeninde olduğu gibi kodumuz yukarıdan aşağıya doğru çalışmaya devam ediyor ve “c” fonksiyonuna gelindiğinde bu fonksiyon için belirtilmiş olan kod bloğu çalışıyor yani bizim durumumuzda konsola “Merhaba” yazdırmış oluyoruz ardından kodumuz aynı düzende çalışmaya devam ediyor.

Akıllarda “Bu fonksiyonun parametre almasını ya da geriye bir değer döndürmesini istediğimizde nasıl yazacağız?” gibi bir soru olması normal. Bunun için bir higher-order fonksiyon yazalım:

fun foo(a: Int, b: String, c: (String, String) -> String) {
println(a)
val cReturn = c("Merhaba,", "Dünya!")
println(cReturn)
println(b)
}

İki tane String bekleyen ve geri dönüşü de String olan bir fonksiyonu parametre olarak bekliyoruz ve fonksiyon içerisinde bu geri dönüş değerini yazdırıyoruz. Fonksiyon tiplerini nasıl yazacağımızı bildiğimiz için işin bu kısmı kolay peki bu fonksiyonu nasıl oluşturacağız. Bunun için öncelikle fonksiyonları daha iyi incelememiz gerekiyor.

Kotlin’de fonksiyonlar birinci sınıf nesnelerdir(first class citizen), yani bunlar değişkenlerde ve veri yapılarında saklanabilir, diğer higher-order fonksiyonlara argüman olarak verilebilir ve onlardan döndürülebilir. Fonksiyonlar üzerinde, diğer değerler için mümkün olan tüm işlemleri gerçekleştirebilirsiniz.

Buradan anlaşıldığı gibi biz de bu sayede higher-order fonksiyonlara fonksiyonları parametre olarak verebiliyoruz. Instance olarak bir fonksiyonu kullanmanın farklı yöntemleri var bunları inceleyip higher-order fonksiyonumuzda nasıl kullanabileceğimizi görelim.

Lambda Gösterimi (Lambda Expression)

Bu gösterimi anlamak için bir örnek üzerinden konuşalım.

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

Örnekte lambda gösterimi, fonksiyonların tip belirtimi için kullanılan yapıya benziyor gibi görünüyor ancak aralarında birçok fark bulunmaktadır. Bu farkları lambda gösteriminin kurallarını inceledikten sonra daha iyi anlayacağımızı düşünüyorum:

  • Bir lambda gösterimi her zaman küme parantezleriyle çevrilidir.
  • Tam sözdizimsel(syntactic) formdaki parametre bildirimleri süslü parantezler içine alınır ve isteğe bağlı tür belirtimleri içerir. Eğer ki tam sözdizimsel formu bozup fonksiyonun tip belirtimini yapmazsak parametrelerin tipini belirtmek zorunludur.
  • Fonksiyonun body’si “->” işaretlerinden sonra yazılır.
  • Eğer açık tip gösterimi ile geri dönüş değeri Unit olarak belirtilmezse ya da tip çıkarımı ile geri dönüş değeri Unit olarak belirlenmezse, lambda body’sindeki sonuncu ifade geri dönüş değeri olarak kabul edilir.

Eğer derleyici gösterimi parametresiz olarak analiz edebiliyorsa, parametre bildirilmesine gerek kalmaz ve “->” ifadesi atlanır. Parametre, otomatik olarak “it” adı ile bildirilecektir.

val doubleTheNumber: (Int) -> Int = { it * 2 }

Lambda gösterimini incelediğimize göre artık oluşturmuş olduğumuz fonksiyonda kullanabiliriz:

Fonksiyonumuzu bu şekilde ayrı oluşturup kullanabileceğimiz gibi doğrudan higher-order fonksiyonumuzu çağırırken de oluşturabiliriz:

Anonim Fonksiyonlar(Anonymous functions)

Anonim bir fonksiyon, adının belirtilmemesi dışında normal bir fonksiyon bildirimine çok benzer.

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

Anonim fonksiyonların body’si bir ifade(expression) olabileceği gibi (yukarıda gösterildiği şekilde) bir block da olabilir:

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

Anonim fonksiyonların dönüş tipi çıkarımı(type inference), tıpkı normal fonksiyonlarda olduğu gibi çalışır: dönüş tipi, bir expression body’sine sahip anonim fonksiyonlar için otomatik olarak belirlenir ancak block body’sine sahip anonim fonksiyonlar için açıkça belirtilmesi gerekir(veya Unit olduğu varsayılır).

Anonim fonksiyonları incelediğimize göre artık oluşturmuş olduğumuz fonksiyonda kullanabiliriz:

Fonksiyonumuzu bu şekilde ayrı oluşturup kullanabileceğimiz gibi doğrudan higher-order fonksiyonumuzu çağırırken de oluşturabiliriz fakat bu çağırımı yaparken lambda gösteriminden farklı bir durum vardır:

Anonim bir fonksiyonu parametre olarak verirken, normal parantezler içerisine koymamız gerekir. Fonksiyonu parantezlerin dışarısında bıraktığımız gösterim sadece lambda gösterimlerinde kullanılabilir.

Bu bilgi ile artık fonksiyonumuzu doğrudan parametre olarak verebiliriz:

Fonksiyon referansı(Function reference)

Var olan fonksiyonları parametre olarak vermek için “::” operatörü ile çağırabiliriz.

En kısa anlatım burası olacak çünkü normal bir şekilde oluşturduğumuz fonksiyonun instance’ını oluşturmak için “::” operatörünü kullanmak yeterli oluyor. Lafı daha fazla uzatmadan bu şekilde fonksiyonumuza parametre olarak verirsek nasıl görüneceğini inceleyebiliriz:

Bu şekilde higher-order fonksiyonun nasıl oluşturulduğunu ve ona parametre olarak bir fonksiyonu nasıl verebileceğimizi incelemiş olduk.

Higher-order fonksiyonların genel bir özetini yapmaya çalıştım. Vaktinizi ayırıp yazımı okuduğunuz için teşekkür ederim. Eğer içeriğim hakkında herhangi bir geri bildiriminiz veya sorunuz varsa benimle linkedIn üzerinden iletişime geçebilirsiniz. Sağlıklı ve mutlu kalın!

--

--