Özyineleme, işlevsel dillerde ve Kotlin'de karşılaşılan yararlı bir tekniktir. Ancak, doğrudan özyineleme genellikle yığın taşmasına (StackOverflowError) yol açar. Bununla başa çıkmak için birçok dilde kuyruklu özyineleme optimizasyonu (tail call optimization) uygulanmıştır. Kotlin'de bu, açıkça tailrec anahtar kelimesiyle gerçekleştirilir.
Normal özyineleme, her çağrıda yeni yığın çerçeveleri oluşturur. Özyineleme derinliği büyük olduğunda, bu StackOverflowError'a yol açar. Kuyruklu özyineleme otomatik optimizasyonu her zaman mümkün değildir ve JVM, bazı diğer dillerin derleyicileri gibi desteklemez (Scala, Erlang gibi).
Kotlin, bir işlevi tailrec anahtar kelimesiyle işaretlemeyi sağlar. Eğer işlev gerçekten kuyruklu bir özyineleme ise ("tail call" — kendisine yapılan çağrı son işlem ise), derleyici bunu bir döngü ile değiştirir, böylece yığın taşması ortadan kalkar.
Kod örneği:
tailrec fun factorial(n: Int, acc: Int = 1): Int = if (n == 0) acc else factorial(n - 1, acc * n) println(factorial(5)) // 120
Anahtar özellikler:
Eğer tailrec varsa ama çağrı kuyruklu pozisyonda değilse ne olur?
Derleyici bir hata verir ve optimizasyonu uygulamaz.
tailrec fun sum(n: Int, acc: Int = 0): Int { println(n) // buradan sonra optimizasyon mümkün değil return if (n == 0) acc else sum(n - 1, acc + n) } // Derleme hatası: çağrı kuyruklu pozisyonda değil
tailrec açık işlevlerde veya soyut yöntemlerde kullanılabilir mi?
Hayır, anahtar kelime yalnızca final işlevlerde çalışır.
open class Base { // Hata: // tailrec open fun test() {} }
Normal özyineleme ile tailrec arasında performans farkı var mı?
Evet, tailrec işlevi bir döngüye dönüştürerek yığın üzerindeki yükü azaltır ve StackOverflowError'u ortadan kaldırır.
Geliştirici, kullanıcılarına başvurarak tüm özyinelemeli işlevleri tailrec ile işaretler, optimizasyonun uygulanabilir olup olmadığını düşünmeden. Çağrılar her zaman kuyruklu pozisyonda değildir — sonuçta projeler derlenemez ve özyineleme verimli çalışmaz.
Artılar:
Geliştirici mantığı inceler, ağaç araması veya faktöriyel gibi işlevlerde kuyruklu özyinelemeyi kullanır. tailrec, bu gerçekten mümkün olduğunda kullanılır. Yığın taşması olmadan etkili bir şekilde derlenmiş bir döngü oluşur.
Artılar: