2016-10-30 1 views
4

ich mich nicht in der Lage hat dieses Beispiel einer rekursiven Funktion zu verstehen:Wie funktioniert diese rekursive Funktion lösen und warum tut er ausgegeben, was es tut

function foo(i) { 
    if (i < 0) 
    return; 
    console.log('begin:' + i); 
    foo(i - 1); 
    console.log('end:' + i); 
} 
foo(3); 

Die Ausgabe lautet:

begin:3 
begin:2 
begin:1 
begin:0 
end:0 
end:1 
end:2 
end:3 

Ich verstehe Wie normal und verschachtelte Funktionen funktionieren, und ich denke, die return; hier soll die Funktion beenden, wenn i wird niedriger als 0, also wenn i = -1, die erste console.log() nicht angezeigt, aber warum nach foo(-1 - 1) wir erhalten die Ausgabe end:0?

+0

es ist i-1 nicht -1-1 und in i = -1 wird nichts ausgegeben – paolobasso

+0

Auf Ihrem Code, wenn Sie 'foo (-1-1)' oder 'foo (-2)' Sie erhalten ' undefiniert. –

+1

Wenn "i = -1", kehrt die Funktion sofort zurück und es wird überhaupt nichts angezeigt. Es ruft nie 'foo (-1 - 1)' auf, stattdessen kehrt es dorthin zurück, wo es aufgerufen wurde - was die Zeile 'foo (i - 1)' in dem Aufruf ist, wo 'i' '0' war. – Bergi

Antwort

6

Um zu verstehen, müssen Sie den Stapel visualisieren. Lassen Sie mich durch den Ausführungsprozess nehmen:

  1. Wir beginnen foo(3) durch den Aufruf, so i 3. Da i ist nicht kleiner als 0, log begin:3. Anruf foo(2)
  2. i ist jetzt 2. Seit i ist nicht weniger als 0, log begin:2. Anruf foo(1)
  3. i ist jetzt 1. Seit i ist nicht weniger als 0, log begin:1.Anruf foo(0)
  4. i ist jetzt 0. Da i nicht weniger als 0 ist, log begin:0. Anruf foo(-1)
  5. i ist jetzt -1. Da i weniger als 0 ist, kehren wir zurück und gehen den Stapel nach oben. Weitermachen, wo wir aufgehört hat, das zweite Protokoll in foo(0):

    console.log('end:' + i); 
    

    end:0 angemeldet ist, weil i auf 0 foo(0) hat beschlossen, gleich ist, nach oben den Stapel zu foo(1)

  6. weiter vom zweiten Protokoll in foo(1). end:1 wird protokolliert, da i gleich 1 ist. foo(1) hat behoben, gehen Sie den Stapel zu foo(2)
  7. Weiter von der zweiten Anmeldung foo(2). end:2 wird protokolliert, weil i gleich 2 ist. foo(2) hat aufgelöst, gehen Sie den Stapel zu foo(3).
  8. Fortfahren von der zweiten Anmeldung foo(3). end:3 wird protokolliert, weil i gleich 3 ist. foo(3) hat aufgelöst und somit ist der Anruf vollständig gelöst.

Dies ergibt:

begin:3 //Step 1 
begin:2 //Step 2 
begin:1 //Step 3 
begin:0 //Step 4 
end:0 //Step 5 
end:1 //Step 6 
end:2 //Step 7 
end:3 //Step 8 

, nun die Frage zu beantworten:

aber warum nach foo (-1 - 1) wir das Ausgangsende erhalten: 0?

Wir rufen nie foo(-1 - 1), weil foo(-1) sofort zurückgibt - es ist der Basisfall. Der Grund, warum es beginnt, end:i zu loggen, wobei i aufsteigend ist, liegt daran, dass die Ausführung dort fortgesetzt wird, wo sie aufgehört hat, bevor Sie rekursiv waren, und foo(i - 1) aufgerufen wurde. Folglich protokolliert es end:i und Anrufe werden dann aufgelöst.

+0

Vielen Dank für die ausführliche Erklärung, ich weiß, ich sollte verstehen, was Sie gesagt haben, aber ich bin verwirrt, ich bin in Schritt 5 verloren, ich weiß nicht, wie 'ich 'wird 0, 1, 2 dann 3 sein . 'i' geht nur von 3 nach 0 (weil foo (i-1) aufgerufen wird) und wenn 'i' -1 erreicht haben, verlassen wir. Also sollte das erste Protokoll log beginnen: 3,2,1,0 und das zweite sollte dasselbe ausgeben. – dwix

+0

@dwix Wenn eine Funktion aufgerufen wird, wird sie auf den Aufruf-Stack gesetzt. Wenn es aufgelöst ist (das ist beendet), wird es vom Stapel durch Last-In-First-Out (LIFO) beendet. Wenn wir also zu Schritt 4 kommen, rufen wir 'foo (-1)' auf. Das löst sich sofort auf, weil -1 kleiner als 0 ist und wir zurückkehren. Sobald wir zurückkommen, wird der Methodenaufruf vom Stapel entfernt und wir kehren zum Aufrufer zurück. – Li357

+0

Der Aufrufer war in diesem Fall 'foo (0)'. Wenn wir eine Funktion aufrufen, führen wir die Funktion aus und gehen dann zu der Stelle zurück, an der wir im Aufrufer aufgehört haben. Da wir kurz vor dem zweiten Baumstamm aufgehört haben, fahren wir von dort weiter. 'i' ist eine lokale Variable in der Funktion. Das "i" in jedem Funktionsaufruf ist vollständig getrennt. Wenn wir nach oben gehen, bleibt der 'i' derselbe wie bei diesem bestimmten Anruf. – Li357

2

Tatsächlich stoppt die Funktion, wenn i = 0, aber da foo (i-1) vor console.log aufgerufen wird ('end:' + i); die Ausgabe aller console.log ('begin:' + i); vor dem Ende angezeigt werden, werden mit dem i-Wert angezeigt.

Tat wirklich das, was hier passiert ist:

  • foo (3)
    • i = 3 -> Anzeige: "beginnen 3";
    • Anruf foo (2)
      • i = 2 -> Anzeige: "2 beginnen";
      • Anruf foo (1)
        • i = 1 -> Anzeige: "1 beginnen";
        • Anruf foo (0)
          • i = 0 -> Anzeige: "0 beginnen";
          • Anruf foo (-1) -> zurück
          • zu foo Zurück (0), Anzeige
          • Ende foo "0 end" (0)
        • zurück zu foo gehen (1), Anzeige
        • Ende foo (1) ...
01 "1 end"

Und so weiter.

+1

Danke, was meinst du im Schritt 'Gehe zurück zu foo (0), zeige" end 0 "an, wie und wo es rückwärts geht? – dwix

+1

Es geht nicht rückwärts, die Funktion foo (-1) ist innerhalb der Funktion foo (0) und nach dem Aufruf von foo (-1) gibt es "console.log ('end' + i)". – VDarricau