173 lines
4.1 KiB
Markdown
173 lines
4.1 KiB
Markdown
---
|
|||
|
|
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<sayi>, 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<sayi>, 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
|
||
|
|
```
|