Rekursion ist ein schwieriges Thema, und ich glaube nicht, dass ich es hier voll und ganz gerecht werden kann. Stattdessen werde ich versuchen, mich auf den bestimmten Code zu konzentrieren, den Sie hier haben, und sowohl die Intuition für die Funktionsweise der Lösung als auch die Mechanik der Berechnung des Ergebnisses durch den Code zu beschreiben.
Der Code, den Sie hier angegeben haben, löst das folgende Problem: Sie möchten die Summe aller Ganzzahlen von a bis b einschließlich kennen. Für Ihr Beispiel möchten Sie die Summe der Zahlen von 2 bis einschließlich 5, einschließlich
2 + 3 + 4 + 5
Wenn Sie versuchen, ein Problem rekursiv zu lösen, sollte einer der ersten Schritte darin bestehen, herauszufinden, wie das Problem in ein kleineres Problem mit derselben Struktur zerlegt werden kann. Nehmen wir also an, Sie wollten die Zahlen von 2 bis einschließlich 5 zusammenfassen. Eine Möglichkeit, dies zu vereinfachen, besteht darin, festzustellen, dass die obige Summe als umgeschrieben werden kann
2 + (3 + 4 + 5)
Hier ist (3 + 4 + 5) zufällig die Summe aller ganzen Zahlen zwischen 3 und 5 einschließlich. Mit anderen Worten, wenn Sie die Summe aller Ganzzahlen zwischen 2 und 5 wissen möchten, berechnen Sie zunächst die Summe aller Ganzzahlen zwischen 3 und 5 und addieren Sie dann 2.
Wie berechnet man also die Summe aller ganzen Zahlen zwischen 3 und 5 einschließlich? Nun, diese Summe ist
3 + 4 + 5
was stattdessen als gedacht werden kann
3 + (4 + 5)
Hier ist (4 + 5) die Summe aller ganzen Zahlen zwischen 4 und 5 einschließlich. Wenn Sie also die Summe aller Zahlen zwischen 3 und 5 einschließlich berechnen möchten, berechnen Sie die Summe aller ganzen Zahlen zwischen 4 und 5 und addieren Sie dann 3.
Hier gibt es ein Muster! Wenn Sie die Summe der ganzen Zahlen zwischen a und b einschließlich berechnen möchten, können Sie Folgendes tun. Berechnen Sie zunächst die Summe der ganzen Zahlen zwischen a + 1 und b einschließlich. Fügen Sie als Nächstes ein zu dieser Summe hinzu. Sie werden feststellen, dass "Berechnen Sie die Summe der ganzen Zahlen zwischen a + 1 und b einschließlich" so ziemlich das gleiche Problem ist, das wir bereits zu lösen versuchen, aber mit leicht unterschiedlichen Parametern. Anstatt von a nach b einschließlich zu berechnen, berechnen wir von a + 1 nach b einschließlich. Das ist der rekursive Schritt - um das größere Problem zu lösen ("Summe von a nach b, einschließlich"), reduzieren wir das Problem auf eine kleinere Version von sich selbst ("Summe von a + 1 bis einschließlich b").
Wenn Sie sich den Code oben ansehen, werden Sie feststellen, dass dieser Schritt darin enthalten ist:
return a + sumInts(a + 1, b: b)
Dieser Code ist einfach eine Übersetzung der obigen Logik. Wenn Sie von a nach b einschließlich summieren möchten, summieren Sie zunächst a + 1 bis einschließlich b (das ist der rekursive Aufruf von sumInt
s) und fügen Sie dann hinzu a
.
Natürlich wird dieser Ansatz an sich nicht funktionieren. Wie würden Sie beispielsweise die Summe aller ganzen Zahlen zwischen 5 und einschließlich 5 berechnen? Nun, mit unserer aktuellen Logik würden Sie die Summe aller ganzen Zahlen zwischen 6 und einschließlich 5 berechnen und dann 5 addieren. Wie berechnen Sie also die Summe aller ganzen Zahlen zwischen einschließlich 6 und 5? Nun, mit unserer aktuellen Logik würden Sie die Summe aller ganzen Zahlen zwischen 7 und 5 einschließlich berechnen und dann 6 hinzufügen. Sie werden hier ein Problem bemerken - das geht einfach weiter und weiter!
Bei der rekursiven Problemlösung muss es eine Möglichkeit geben, die Vereinfachung des Problems zu beenden und es stattdessen direkt zu lösen. In der Regel finden Sie einen einfachen Fall, in dem die Antwort sofort ermittelt werden kann, und strukturieren dann Ihre Lösung, um einfache Fälle direkt zu lösen, wenn sie auftreten. Dies wird normalerweise als Basisfall oder rekursive Basis bezeichnet .
Was ist der Grund für dieses spezielle Problem? Wenn Sie ganze Zahlen von a bis einschließlich b aufsummieren und a größer als b ist, lautet die Antwort 0 - es gibt keine Zahlen im Bereich! Daher strukturieren wir unsere Lösung wie folgt:
- Wenn a> b, ist die Antwort 0.
- Andernfalls (a ≤ b) erhalten Sie die Antwort wie folgt:
- Berechnen Sie die Summe der ganzen Zahlen zwischen a + 1 und b.
- Fügen Sie ein hinzu, um die Antwort zu erhalten.
Vergleichen Sie nun diesen Pseudocode mit Ihrem tatsächlichen Code:
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a + 1, b: b)
}
}
Beachten Sie, dass zwischen der im Pseudocode beschriebenen Lösung und diesem tatsächlichen Code fast genau eine Eins-zu-Eins-Zuordnung besteht. Der erste Schritt ist der Basisfall. Wenn Sie nach der Summe eines leeren Zahlenbereichs fragen, erhalten Sie 0. Andernfalls berechnen Sie die Summe zwischen a + 1 und b und fügen Sie dann a hinzu.
Bisher habe ich nur eine allgemeine Idee hinter dem Code gegeben. Aber Sie hatten zwei andere, sehr gute Fragen. Erstens, warum gibt dies nicht immer 0 zurück, da die Funktion sagt, dass 0 zurückgegeben werden soll, wenn a> b? Zweitens, woher kommen die 14 eigentlich? Schauen wir uns diese der Reihe nach an.
Versuchen wir einen sehr, sehr einfachen Fall. Was passiert, wenn Sie anrufen sumInts(6, 5)
? In diesem Fall sehen Sie beim Durchlaufen des Codes, dass die Funktion nur 0 zurückgibt. Das ist das Richtige, um - es gibt keine Zahlen im Bereich. Versuchen Sie es jetzt noch einmal. Was passiert, wenn Sie anrufen sumInts(5, 5)
? Nun, hier ist was passiert:
- Du rufst an
sumInts(5, 5)
. Wir fallen in den else
Zweig, der den Wert von `a + sumInts (6, 5) zurückgibt.
- Um
sumInts(5, 5)
festzustellen, was sumInts(6, 5)
ist, müssen wir pausieren, was wir tun, und einen Anruf tätigen sumInts(6, 5)
.
sumInts(6, 5)
wird gerufen. Es betritt den if
Zweig und kehrt zurück 0
. Diese Instanz von sumInts
wurde jedoch von aufgerufen sumInts(5, 5)
, sodass der Rückgabewert an sumInts(5, 5)
und nicht an den Anrufer der obersten Ebene zurückgemeldet wird.
sumInts(5, 5)
Jetzt kann man rechnen 5 + sumInts(6, 5)
, um zurück zu kommen 5
. Es gibt es dann an den Anrufer der obersten Ebene zurück.
Beachten Sie, wie der Wert 5 hier gebildet wurde. Wir begannen mit einem aktiven Anruf bei sumInts
. Dadurch wurde ein weiterer rekursiver Aufruf ausgelöst, und der von diesem Aufruf zurückgegebene Wert übermittelte die Informationen an sumInts(5, 5)
. Der Aufruf von sumInts(5, 5)
dann führte wiederum einige Berechnungen durch und gab einen Wert an den Anrufer zurück.
Wenn Sie dies mit versuchen sumInts(4, 5)
, geschieht Folgendes:
sumInts(4, 5)
versucht zurückzukehren 4 + sumInts(5, 5)
. Dazu ruft es auf sumInts(5, 5)
.
sumInts(5, 5)
versucht zurückzukehren 5 + sumInts(6, 5)
. Dazu ruft es auf sumInts(6, 5)
.
sumInts(6, 5)
gibt 0 zurück zu sumInts(5, 5).</li>
<li>
sumInts (5, 5) now has a value for
sumInts (6, 5) , namely 0. It then returns
5 + 0 = 5`.
sumInts(4, 5)
hat jetzt einen Wert für sumInts(5, 5)
, nämlich 5. Es kehrt dann zurück 4 + 5 = 9
.
Mit anderen Worten, der zurückgegebene Wert wird gebildet, indem die Werte einzeln aufsummiert werden, wobei jedes Mal ein Wert verwendet wird, der von einem bestimmten rekursiven Aufruf an zurückgegeben wird sumInts
und der aktuelle Wert von addiert wird a
. Wenn die Rekursion ihren Tiefpunkt erreicht, gibt der tiefste Aufruf 0 zurück. Dieser Wert verlässt die rekursive Aufrufkette jedoch nicht sofort. Stattdessen wird der Wert nur eine Ebene darüber an den rekursiven Aufruf zurückgegeben. Auf diese Weise fügt jeder rekursive Aufruf nur eine weitere Zahl hinzu und gibt sie weiter oben in der Kette zurück, was mit der Gesamtsumme gipfelt. Versuchen Sie als Übung, dies aufzuspüren sumInts(2, 5)
, womit Sie beginnen wollten.
Hoffe das hilft!