--- baslik: Özyineleme bolum: Fonksiyonlar ozet: Kendini çağıran fonksiyonlar, durma koşulu ve klasik örnekler --- Bazı problemler kendisinin daha küçük bir kopyasını içerir: 5! = 5 × 4!, bir klasörün boyutu = içindeki dosyalar + alt klasörlerin boyutu. Böyle problemleri, kendini çağıran fonksiyonlarla (özyineleme) çok doğal çözersin. ## Faktöriyel n! = n × (n-1)! ve 1! = 1. Fonksiyon kendini bir küçük sayıyla çağırır; n 1'e inince durur. ```kbl fonksiyon faktoriyel(n: sayi) -> sayi: eger n <= 1: don 1 don n * faktoriyel(n - 1) yazdir(faktoriyel(5)) yazdir(faktoriyel(10)) ``` ```cikti 120 3628800 ``` Her özyinelemeli fonksiyonun iki parçası vardır: 1. **Durma koşulu:** cevabın doğrudan bilindiği en küçük durum (`n <= 1` ise 1). 2. **Küçülten adım:** problemi biraz küçültüp fonksiyonu yeniden çağırmak (`faktoriyel(n - 1)`). > **Dikkat:** Durma koşulunu unutursan ya da adım problemi küçültmezse fonksiyon kendini sonsuza dek çağırır ve program çöker. Her çağrının durma koşuluna biraz daha yaklaştığından emin ol. ## Adım adım izlemek Ne olduğunu görmek için fonksiyonun içine `yazdir` koyalım. ```kbl fonksiyon geri_say(n: sayi): eger n == 0: yazdir("Kalkış!") don yazdir(n) geri_say(n - 1) geri_say(3) ``` ```cikti 3 2 1 Kalkış! ``` Bu fonksiyon değer döndürmez; tek başına `don` yazmak fonksiyondan çıkmak demektir. ## Basamakların toplamı 4729 → 4 + 7 + 2 + 9. Son basamak `n % 10`, geri kalan `n // 10`'dur. ```kbl fonksiyon basamak_toplami(n: sayi) -> sayi: eger n < 10: don n don n % 10 + basamak_toplami(n // 10) yazdir(basamak_toplami(4729)) ``` ```cikti 22 ``` ## Fibonacci Her sayı kendinden önceki iki sayının toplamıdır: 0, 1, 1, 2, 3, 5, 8... ```kbl fonksiyon fib(n: sayi) -> sayi: eger n < 2: don n don fib(n - 1) + fib(n - 2) dongu i icinde 0..10: konsol.yaz(f"{fib(i)} ") yazdir() ``` ```cikti 0 1 1 2 3 5 8 13 21 34 55 ``` > **İpucu:** Bu Fibonacci fonksiyonu aynı değerleri defalarca hesaplar; `fib(40)` gibi büyük değerlerde çok yavaşlar. Özyineleme her zaman en hızlı yol değildir; aynı işi bir `iken` döngüsüyle de yapabilirsin. ## Metni ters çevirmek Özyineleme yalnız sayılarla çalışmaz. Bir metnin tersi = ilk harf hariç kalanın tersi + ilk harf. ```kbl fonksiyon ters(m: metin) -> metin: eger m.uzunluk() <= 1: don m don ters(m.dilim(1, m.uzunluk() - 1)) + m[0] yazdir(ters("kalem")) yazdir(ters("Ey Edip")) ``` ```cikti melak pidE yE ``` ## Hanoi kuleleri n diski A'dan C'ye taşımak için: n-1 diski B'ye taşı, en büyüğünü C'ye taşı, n-1 diski B'den C'ye taşı. Üç satırlık bir tanım, döngüyle yazması zor bir işi çözer. ```kbl fonksiyon hanoi(n: sayi, kaynak: metin, hedef: metin, ara: metin): eger n == 0: don hanoi(n - 1, kaynak, ara, hedef) yazdir(f"{n}. diski {kaynak} → {hedef}") hanoi(n - 1, ara, hedef, kaynak) hanoi(3, "A", "C", "B") ``` ```cikti 1. diski A → C 2. diski A → B 1. diski C → B 3. diski A → C 1. diski B → A 2. diski B → C 1. diski A → C ``` ## Alıştırma ```alistirma `us(taban: sayi, kuvvet: sayi) -> sayi` fonksiyonunu özyinelemeyle yaz: `us(t, 0)` 1'dir, `us(t, k)` ise `t * us(t, k - 1)`'dir. `us(2, 10)` ve `us(3, 4)` sonuçlarını yazdır. %%% # buraya yaz yazdir(us(2, 10)) yazdir(us(3, 4)) %%% fonksiyon us(taban: sayi, kuvvet: sayi) -> sayi: eger kuvvet == 0: don 1 don taban * us(taban, kuvvet - 1) yazdir(us(2, 10)) yazdir(us(3, 4)) %%% 1024 81 ``` ```alistirma `liste_toplami(l: liste, i: sayi) -> sayi` fonksiyonunu özyinelemeyle yaz: listenin `i`'nci öğesinden sonuna kadar olan öğelerin toplamını döndürsün. `i` listenin uzunluğuna eşitse 0 döndür. %%% # buraya yaz yazdir(liste_toplami([4, 8, 15, 16, 23, 42], 0)) %%% fonksiyon liste_toplami(l: liste, i: sayi) -> sayi: eger i == l.uzunluk(): don 0 don l[i] + liste_toplami(l, i + 1) yazdir(liste_toplami([4, 8, 15, 16, 23, 42], 0)) %%% 108 ```