13 Stimmen

Umschreiben einer rekursiven Funktion ohne Verwendung der Rekursion

Ich schreibe einen bestehenden Code in einer Umgebung um, in der rekursive Aufrufe weder einfach implementiert noch gewünscht sind. (Und in Fortran 77, falls Sie es wissen müssen.) Ich habe darüber nachgedacht, einen Stack von Grund auf neu zu erstellen, um die benötigten Aufrufe im Auge zu behalten, aber das scheint umständlich zu sein, und ich würde lieber keinen Speicher für ein Array in Fällen zuweisen, in denen die Rekursion nicht tief ist. (Ich bin nicht zuversichtlich, dass Fortran 77 auch dynamische Array-Größe unterstützt.)

Irgendwelche anderen Vorschläge für eine allgemeine Lösung, wie man eine offensichtlich rekursive Funktion nimmt und sie nicht-rekursiv umschreibt, ohne Platz auf einem Stapel zu verschwenden?

Vielen Dank! Alter McSt

2 Stimmen

Wenn sie nicht verzweigt, können Sie sie normalerweise in eine einfache Schleife umschreiben. Wenn sie verzweigt, brauchen Sie normalerweise einen Stapel

1 Stimmen

@CodeInChaos: Eine rekursive Funktion, die nicht verzweigt, wird per Definition niemals zurückkehren...

0 Stimmen

Ich glaube, ich habe das Wort Verzweigung falsch verwendet. Ich meine damit, dass er sich selbst mehrfach aufruft, so dass der Graph der Aufrufe zu einem Baum mit Verzweigungen wird. Und es ist nur meine Erfahrung und nicht immer wahr.

18voto

Victor Nicollet Punkte 23939

Wenn Ihr Code Tail-Rekursion verwendet (d.h. die Funktion gibt das Ergebnis jedes rekursiven Aufrufs direkt zurück, ohne weitere Verarbeitung), dann ist es möglich, die Funktion zwingend ohne Stack umzuschreiben:

function dosomething(a,b,c,d) 
{
  // manipulate a,b,c,d
  if (condition) 
    return dosomething(a,b,c,d) 
  else
    return something;
}

In:

function dosomething(a,b,c,d) 
{
  while (true) 
  {
    // manipulate a,b,c,d
    if (condition) continue;
    else return something;
  }
}

Ohne Tail-Rekursion ist die Verwendung eines Stacks (oder eines ähnlichen Zwischenspeichers) die einzige Lösung.

0 Stimmen

Das ist ähnlich wie das, was ich dachte, aber nicht in Worte fassen konnte. In meinem speziellen Fall habe ich also einen Datensatz mit Elementen, die jeweils auf eine Beziehung zu anderen Elementen des Satzes getestet werden müssen. Ich könnte die Datenstruktur aller Elemente übergeben, aber da jedes Element weiter verarbeitet werden muss, ist der Stapel wohl unvermeidlich, oder?

0 Stimmen

Kommt drauf an. Wenn der Code hauptsächlich aus "für alle Paare (a,b), wenn P(a,b) wahr ist, dann F(a,b) ausführen" besteht, können Sie mit einer iterativen Schleife durch alle Paare auskommen...

3voto

Jason S Punkte 178087

Die klassische rekursive Funktion, die als Schleife geschrieben werden kann, ist die Fibonacci-Funktion:

 function fib(n)
 {
     // valid for n >= 0
     if (n < 2)
         return n;
     else
         return fib(n-1) + fib(n-2);
 }

Ohne Memoisierung erfordert dies jedoch O(exp^N) Operationen mit O(N) Stapelplatz.

Sie kann umgeschrieben werden:

 function fib(n)
 {
     if (n < 2)
        return n;
     var a = 0, b = 1;
     while (n > 1)
     {
         var tmp = a;
         a = b;
         b = b + tmp;
         n = n - 1;
     }   
     return b;
 }

Dazu muss man jedoch wissen, wie die Funktion funktioniert. Ich bin mir nicht sicher, ob dies auf einen automatischen Prozess verallgemeinert werden kann.

2voto

Ofir Punkte 8039

Die meisten rekursiven Funktionen können leicht in Schleifen umgeschrieben werden, um Platz zu sparen - das hängt von der Funktion ab, da viele (aber nicht alle) rekursive Algorithmen tatsächlich auf diese Art von Speicher angewiesen sind (obwohl die Schleifenversion auch in diesen Fällen normalerweise effizienter ist).

0 Stimmen

Könnten Sie eine rekursive Funktion zeigen, die keinen Platz auf dem Stack benötigt? Auch nicht für ihre Argumente?

2 Stimmen

@Nikita Rybak : eine inlined tail-recursive Funktion ;)

0 Stimmen

@Victor Ja, aber das ist in umgeschriebener Form. Und Ofir behauptet, es gäbe eine rekursive Funktion, die keinen Stapelspeicher benötigt. Also, ich bin neugierig.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X