Seite 1 von 1

Kleine Frage zum Code

Verfasst: Dienstag 6. Dezember 2022, 19:16
von Kahnbein.Kai
Guten Abend,
es geht um folgenden Codeschnipsel:

Code: Alles auswählen

def z(c, n):
    return c if n == 0 else z(c, n-1) ** 2 + c
   
print([z(5, n) for n in range(10)])
Ich verstehe hier folgende Stelle nicht

Code: Alles auswählen

else z(c, n-1) ** 2 + c
Die Funktion z wird mit c = 5 und n von 0 bis 9 gefüllt und dann mit 2 multipliziert plus 5 solange n nicht 0 wird.
Warum kann ich eine Funktion z(c, n-1) multiplizieren ?
Leider kann ich mir die Werte nicht ausgeben lassen, bei

Code: Alles auswählen

print(z(c,n-1)
kommt diese Fehlermeldung :

Code: Alles auswählen

RecursionError: maximum recursion depth exceeded
Ich habe mal versucht die Funktion so umzuschreiben wie ich es machen würde, so verstehe ich es auch.

Code: Alles auswählen

def z(c, n):
    c0 = c
    print("Zo: "+ str(c))
    for i in range(n):
        c = c ** 2 + c0
        print("z" + str(i+1)+ " " + str(c))



z(5,10)    
Gruß Kai

Re: Kleine Frage zum Code

Verfasst: Dienstag 6. Dezember 2022, 19:52
von Sirius3
Du multiplizierst nicht eine Funktion, sondern den Rückgabewert des Funktionsaufrufs.
Das `a if b else c`-Konstrukt ist das selbe wie:

Code: Alles auswählen

def z(c, n):
    if n == 0:
        x = c
    else:
        x = z(c, n-1) ** 2 + c
    return x
also ein ganz simpler rekursiver Algorithmus.
Und hier kannst Du auch ohne weiteres x ausgeben, um zu sehen, was da genau passiert.

Re: Kleine Frage zum Code

Verfasst: Dienstag 6. Dezember 2022, 19:55
von __blackjack__
@Kahnbein.Kai: Hättest Du die Frage „Warum kann ich mit einer Funktion multiplizieren“ auch bei ``x = sin(0.5) * 3``? Es wird ja nicht die *Funktion* mit einer Zahl multipliziert, sondern die Funktion wird aufgerufen und das *Ergebnis* wird mit einer Zahl multipliziert. Und genau das gleiche passiert auch mit dem `z()` — da wird eine Funktion, hier halt wieder die Funktion in der das als Teilausdruck steht → Rekursion, aufgerufen, aber ansonsten ist das nicht anderes als das Beispiel mit dem `sin()`-Aufruf.

Da das eine reine Funktion ist, also Eingabewerte hat, einen Rückgabewert, und keine Seiteneffekte, kann man das einfach durch ersetzen visualisieren:

Nehmen wir mal den konkreten Fall ``z(5, 2)``:

Code: Alles auswählen

z(5, 2)
# Die Werte eingesetzt:
5 if 2 == 0 else z(5, 2-1) ** 2 + 5
# Da 2≠0 ist, bleibt der ``else``-Teil:
z(5, 1) ** 2 + 5
# Für `z` wieder die Werte eingesetzt:
(5 if 1 == 0 else z(5, 1-1) ** 2 + 5) ** 2 + 5
# Da 1≠0 ist, bleibt auch hier wieder der ``else``-Teil:
(z(5, 0) ** 1 + 5) ** 2 + 5
# Für `z` wieder die Werte eingesetzt:
((5 if 0 == 0 else z(5, 0-1) ** 2 + 5) ** 2 + 5) ** 2 + 5
# Dieses mal wird der ``if``-Teil genommen:
((5) ** 2 + 5) ** 2 + 5
# Und das kann man jetzt einfach ausrechnen:
905
Würde man real tatsächlich mit einer Schleife lösen, weil das mit Rekursion zu machen in Programmiersprachen, die keine Optimierung für endrekursive Aufrufe garantieren, („tail call optimisation“) ein Stackoverflow ist, der nur darauf wartet das Programm abstürzen zu lassen. Beziehungsweise in Python der `RecursionError`, den Du bereits kennengelernt hast.

In der Mathematik gibt man so etwas gerne rekursiv an, weil man dann nicht so etwas wie „Schleifen“ erfinden muss, sondern eine normale, halt rekursiv definierte, Formel angeben kann. Und es gibt funktionale Programmiersprachen, beispielsweise Haskell, die keine prozeduralen Schleifenkonstrukte haben, wo man so etwas dann auch rekursiv angeben kann.

Re: Kleine Frage zum Code

Verfasst: Mittwoch 7. Dezember 2022, 08:03
von Kahnbein.Kai
Guten Morgen,
super Danke für die ausführliche Erklärung, jetzt habe ich es verstanden !


Gruß Kai