Kleine Frage zum Code

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

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
Sirius3
User
Beiträge: 17778
Registriert: Sonntag 21. Oktober 2012, 17:20

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.
Benutzeravatar
__blackjack__
User
Beiträge: 13159
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Guten Morgen,
super Danke für die ausführliche Erklärung, jetzt habe ich es verstanden !


Gruß Kai
Antworten