Python Recursive fonksiyon kullanımı

796110

Femtopat
Katılım
12 Haziran 2024
Mesajlar
42
Recursive (özyinelemeli) fonksiyonları anlayabileceğim şekilde ne için kullanıldığını örneklerle anlatabilir misiniz? Basit ve anlaşılır örnekler olmalı.
 
Ozyinelemeli fonksiyonlar kendilerini cagiran fonksiyonlar. Bir fonksiyon'u cagirdiginda, onun icindeki tum satirlari tek tek calistiriyorsun. Oz yinelemeli fonksiyonda da kendisini tekrar cagirdigi icin, tekrara giriyor. Basit looplar olarak kullaniliyor.

Soyle;
Python:
def foo():
    foo();

Simdi sen yukaridaki arkadasi cagirdiginda, hic bir base case olmadigi icin sonsuz bir loop olusturacaksin. Ayni while True: gibi. Yaptigi is ise su olacak.
foo() -> foo() -> foo() -> foo() -> ...
(Yukaridakilerin her biri fonksiyon cagrisi)

Bunu kirmak icin bir kosul eklemelisin. Bir base case.
Python:
def foo(bar: int):
    if bar == 0:
        return
    foo(bar - 1)

Yukaridaki kodu foo(3) olarak cagirdigini dusunelim. Asagidaki gibi calisacak;
foo(3) -> foo(2) -> foo(1) -> foo(0) -> return

Once foo(3) olarak cagirdik, sonrasinda if kosulu saglanmadigi icin 2 olarak cagrildi, sonrasinda 1 olarak, en son sifir olarak. Sifir olarak cagirdigimizda kosul saglandigi icin return gerceklesti.

Bunu bir tur hesaplama icin kullanabiliriz.
Python:
def factorial(n: int):
    if n == 0:
        return 1
    return factorial(n - 1) * n
Simdi yukaridakine bakalim;
factorial(3) olarak cagiralim.
Kod:
factorial(3)
-> factorial(2)
    -> factorial(1)
        -> factorial(0)
            -> return 1
        -> return 1 * 1
    -> return 1 * 1 * 2
-> return 1 * 1 * 2 * 3
-> 6
Bu da soyle olurdu eger carpmalari da execute edersek.
Kod:
factorial(3)
-> factorial(2)
    -> factorial(1)
        -> factorial(0)
            -> return 1
        -> return 1
    -> return 2
-> return 6
-> 6

Neden once azalan sirada cagrilari, sonra artan sirada return'leri yazdim? Cunke distan ice execution baslayacak. Bir satirdaki komut soldan saga dogru execute edilir, soldaki komut bitmeden sagdaki komut calismayacak. Soldaki komut ise kendi icindekini bitirmeden bitmis olmayacak. Zincirleme sekilde disaridan iceriye dogru fonksiyonlari tetikledik. Dolayisiyla once en icteki, sonra bir ustundeki, sonra bir ustunde, sonra bir ustundeki bitmis oldu.

Eger return'deki yerleri degistirseydik, carpma isleminin gerceklesebilmesi icin yine en icteki islemin bitmesi gerekirdi ama return ters gozukurdu. Soyle test edebilirsin;
Python:
returns = []

def printReturns():
    print("return ", end="")
    for i, r in enumerate(returns):
        print(f"{r}", end=" * " if len(returns) > 0 and i < len(returns) - 1 else "")
    print()

def append(i: int) -> int:
    returns.append(i)
    printReturns()
    return i

def factorial(n: int):
    if n == 0:
        return append(1)
    return factorial(n - 1) * append(n)

def factorialAlt(n: int):
    if n == 0:
        return append(1)
    return append(n) * factorialAlt(n - 1)

factorial(3) # 6
returns = []
factorialAlt(3)

En icten en disa carpma gerceklesecek her zaman. Iki degeride bilmeden carpma yapamaz sonucta.

factorialAlt icin;
Kod:
factorialAlt(3)
-> 3 * factorialAlt(2)
    -> 2 * factorialAlt(1)
        -> 1 * factorialAlt(0)
            -> return 1
        -> return 1 * 1
    -> return 2 * 1 * 1
-> return 3 * 2 * 1 * 1
-> 6
Hesaplanmis;
Kod:
factorial(3)
-> factorial(2)
    -> factorial(1)
        -> factorial(0)
            -> return 1
        -> return 1
    -> return 2
-> return 6
-> 6
Yeterince net anlatabildim mi bilmiyorum. Anlamadigin, takildigin yer olursa sorabilirsin.
 
Ozyinelemeli fonksiyonlar kendilerini cagiran fonksiyonlar. Bir fonksiyon'u cagirdiginda, onun icindeki tum satirlari tek tek calistiriyorsun. Oz yinelemeli fonksiyonda da kendisini tekrar cagirdigi icin, tekrara giriyor. Basit looplar olarak kullaniliyor.

Soyle;
Python:
def foo():
    foo();

Simdi sen yukaridaki arkadasi cagirdiginda, hic bir base case olmadigi icin sonsuz bir loop olusturacaksin. Ayni while True: gibi. Yaptigi is ise su olacak.
foo() -> foo() -> foo() -> foo() -> ...
(Yukaridakilerin her biri fonksiyon cagrisi)

Bunu kirmak icin bir kosul eklemelisin. Bir base case.
Python:
def foo(bar: int):
    if bar == 0:
        return
    foo(bar - 1)

Yukaridaki kodu foo(3) olarak cagirdigini dusunelim. Asagidaki gibi calisacak;
foo(3) -> foo(2) -> foo(1) -> foo(0) -> return

Once foo(3) olarak cagirdik, sonrasinda if kosulu saglanmadigi icin 2 olarak cagrildi, sonrasinda 1 olarak, en son sifir olarak. Sifir olarak cagirdigimizda kosul saglandigi icin return gerceklesti.

Bunu bir tur hesaplama icin kullanabiliriz.
Python:
def factorial(n: int):
    if n == 0:
        return 1
    return factorial(n - 1) * n
Simdi yukaridakine bakalim;
factorial(3) olarak cagiralim.
Kod:
factorial(3)
-> factorial(2)
    -> factorial(1)
        -> factorial(0)
            -> return 1
        -> return 1 * 1
    -> return 1 * 1 * 2
-> return 1 * 1 * 2 * 3
-> 6
Bu da soyle olurdu eger carpmalari da execute edersek.
Kod:
factorial(3)
-> factorial(2)
    -> factorial(1)
        -> factorial(0)
            -> return 1
        -> return 1
    -> return 2
-> return 6
-> 6

Neden once azalan sirada cagrilari, sonra artan sirada return'leri yazdim? Cunke distan ice execution baslayacak. Bir satirdaki komut soldan saga dogru execute edilir, soldaki komut bitmeden sagdaki komut calismayacak. Soldaki komut ise kendi icindekini bitirmeden bitmis olmayacak. Zincirleme sekilde disaridan iceriye dogru fonksiyonlari tetikledik. Dolayisiyla once en icteki, sonra bir ustundeki, sonra bir ustunde, sonra bir ustundeki bitmis oldu.

Eger return'deki yerleri degistirseydik, carpma isleminin gerceklesebilmesi icin yine en icteki islemin bitmesi gerekirdi ama return ters gozukurdu. Soyle test edebilirsin;
Python:
returns = []

def printReturns():
    print("return ", end="")
    for i, r in enumerate(returns):
        print(f"{r}", end=" * " if len(returns) > 0 and i < len(returns) - 1 else "")
    print()

def append(i: int) -> int:
    returns.append(i)
    printReturns()
    return i

def factorial(n: int):
    if n == 0:
        return append(1)
    return factorial(n - 1) * append(n)

def factorialAlt(n: int):
    if n == 0:
        return append(1)
    return append(n) * factorialAlt(n - 1)

factorial(3) # 6
returns = []
factorialAlt(3)

En icten en disa carpma gerceklesecek her zaman. Iki degeride bilmeden carpma yapamaz sonucta.

factorialAlt icin;
Kod:
factorialAlt(3)
-> 3 * factorialAlt(2)
    -> 2 * factorialAlt(1)
        -> 1 * factorialAlt(0)
            -> return 1
        -> return 1 * 1
    -> return 2 * 1 * 1
-> return 3 * 2 * 1 * 1
-> 6
Hesaplanmis;
Kod:
factorial(3)
-> factorial(2)
    -> factorial(1)
        -> factorial(0)
            -> return 1
        -> return 1
    -> return 2
-> return 6
-> 6
Yeterince net anlatabildim mi bilmiyorum. Anlamadigin, takildigin yer olursa sorabilirsin.
Çok teşekkür ederim hocam. anladım. Siz nasıl bir çalışma stratejisi izlediniz? Programlama büt'üm var ve kalırsam çok sıkıntı olacak, tavsiyeleriniz varsa dinleyebilirim...
 
Çok teşekkür ederim hocam. anladım. Siz nasıl bir çalışma stratejisi izlediniz? Programlama büt'üm var ve kalırsam çok sıkıntı olacak, tavsiyeleriniz varsa dinleyebilirim...
Programlama sinavlarina ozel olarak hic calismadim universitedeyken. Gunluk derslerden sonra eve gectigimde pratik yapiyordum, odevleri zamaninda yaptim teslim ettim. Bos zamanlarimda oyun oynar gibi kod yazip duruyordum. Yani sinava calismak amaciyla degilde, gunluk hayatimin bir parcasi olarak kullandigim icin ekstra sinav zamani calisma ihtiyaci duymuyordum.
 
Programlama sinavlarina ozel olarak hiç calismadim universitedeyken. Gunluk derslerden sonra eve gectigimde pratik yapiyordum, odevleri zamaninda yaptim teslim ettim. Bos zamanlarimda oyun oynar gibi kod yazip duruyordum. Yani sinava calismak amaciyla degilde, gunluk hayatimin bir parcasi olarak kullandigim icin ekstra sinav zamani calisma ihtiyaci duymuyordum.

darısı başıma .d
 
Ek olarak "fazla derinlere inen" recursive bir fonksiyon yazıp çalıştırmaya kalkarsanız şu hatayı görmeniz mümkün:

Python:
>>> def f(x: int):
...     if x <= 0:
...             return 1
...     return x * f(x - 1)
...
>>> f(1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in f
  File "<stdin>", line 4, in f
  File "<stdin>", line 4, in f
  [Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded

Hata mesajından anlaşılabileceği üzere bir "recursion limiti" mevcut. Detayları merak ederseniz bir StackOverflow başlığı paylaşayım:


Python'da varsayılan olarak en fazla -aşağı yukarı- 1000 recursive çağrı yapabiliyorsunuz. Bu sınırı yükseltmek için sys kütüphanesinin setrecursionlimit metodunu kullanabilirsiniz:

Python:
import sys

sys.setrecursionlimit(10**7) # limiti 10 milyona yukseltiyoruz


def f(x: int):
    if x <= 0:
        return 1
    return x * f(x - 1) % (10**9 + 7)


print(f(int(input())))

Buna rağmen işletim sisteminin sınırlamalarına da yakalanabiliyorsunuz. Benim başıma gelmişti ama pek bilmediğim konular olduğu için bir yorumda bulunamayacağım.
 
Ek olarak "fazla derinlere inen" recursive bir fonksiyon yazıp çalıştırmaya kalkarsanız şu hatayı görmeniz mümkün:

Python:
>>> def f(x: int):
... if x <= 0:
... return 1
... return x * f(x - 1)
...
>>> f(1000)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<stdin>", line 4, in f
 File "<stdin>", line 4, in f
 File "<stdin>", line 4, in f
 [Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded

Hata mesajından anlaşılabileceği üzere bir "recursion limiti" mevcut. Detayları merak ederseniz bir Stack Overflow başlığı paylaşayım:


Python'da varsayılan olarak en fazla -aşağı yukarı- 1000 recursive çağrı yapabiliyorsunuz. Bu sınırı yükseltmek için sys kütüphanesinin setrecursionlimit metodunu kullanabilirsiniz:

Python:
import sys

sys.setrecursionlimit(10**7) # limiti 10 milyona yukseltiyoruz

def f(x: int):
 if x <= 0:
 return 1
 return x * f(x - 1) % (10**9 + 7)

print(f(int(input())))

Buna rağmen işletim sisteminin sınırlamalarına da yakalanabiliyorsunuz. Benim başıma gelmişti ama pek bilmediğim konular olduğu için bir yorumda bulunamayacağım.

Teşekkür ederim. O kadar derinlemesine işlemiyoruz "şu anlık". Yine de bilgi olmuş oldu.
 

Technopat Haberler

Yeni konular

Geri
Yukarı