Ja, 8 ist richtig, aber mit 13 lag ich falsch, das richtige Ergebnis sollte 25 sein. Ich habe den Quelltext etwas umgeschrieben und ausgebessert, damit es einfacher zu erklären wird:
Code: Alles auswählen
def fib(n):
if n == 0:
return (1, 0)
elif n == 1:
return (1, 1)
elif n > 1:
lcalls, lvalue = fib(n-2)
rcalls, rvalue = fib(n-1)
return (lcalls + rcalls + 1, lvalue + rvalue)
print fib(6)
Du hast hier einen rekursiven Aufruf, wobei die rekursive Funktion zwei Rekursionsanker hat. Rekursionsanker sind die Zweige der Funktion, die selbst nicht rekursiv sind, d.h. bei denen die Rekursion demnach terminiert.
Diese Anker sind ``fib(0)``, das hat das Ergebnis ``(1, 0)`` weil das Ergebnis 0 ist und dafür ein Funktionsaufruf nötig war. Der andere Anker gibt ``(1, 1)`` zurück, weil das Ergebnis 1 ist und auch dafür nur ein Funktionsaufruf nötig war.
Nun aber zum Fall dass es nicht 0 oder 1 ist, dann muss die Funktion rekursiv aufgerufen werden. Sagen wir mal, wir wollen ``fib(2)`` bestimmen. Da 2 weder gleich 0 noch gleich 1 ist, kommen wir in den ``else``-Zweig. Was müssen wir also machen? Nun, wir müssen erstmal ``fib(n-2)`` berechnen, also ``fib(2-2)`` also ``fib(0)`` berechnen. Das Ergebnis ist ``(1, 0)``. Jetzt nutzen wir Tuple-Unpacking, damit wir uns die Indexerei sparen und sprechende Namen haben: wir binden die 1 an ``lcalls`` weil es die Anzahl der Funktionsaufrufe des linken Operanden ist, und die 0 an ``lvalue`` weil es der zugehörige Wert ist. Selbiges machen wir mit dem ``fib(n-1)``-Teil, also ``fib(2-1)`` also ``fib(1)`` was ``(1, 1)`` ergibt, das binden wir an die entsprechenden rechts-Varianten der Namen.
Nun müssen wir nur noch die rechte und linke Seite zusammenfassen. Wie sieht das aus? Nunja, der Wert ist klar: das ist die Summe vom linken (n-2) und rechten (n-1) Teil der Rekursion. Das geben wir an der zweiten stelle des Tupels zurück. An der ersten Stelle geben wir die Anzahl der Aufrufe zurück. Die wissen wir: Anzahl der Aufrufe rechts plus die Anzahl der Aufrufe links, da wir aber gerade selbst mitten in einem Funktionsaufruf sind, müssen wir natürlich noch 1 draufzählen. Also geben wir ``(neue_anzahl_aufrufe, neue_fib_summe)`` zurück.
Das wars! Falls du noch Fragen hast: einfach nochmal nachhaken. Übrigens empfehle ich "The Little Schemer" zum lernen von Rekursion, das ist ein kleines Buch, das sich genau damit beschäftigt (ist allerdings mit für Python-Programmierer etwas ungewohnter Syntax).
Yoda sinngemäß: "Um Rekursion zu verstehen, Rekursion verstehen du musst"
