Kann mir jemand den Unterschied zwischen diesen beiden Arten Rekursionen und Beispiel (speziell in OCaml) geben?Tail-Rekursion vs. Vorwärts-Rekursion
Antwort
Eine rekursive Schwanzfunktion ist eine Funktion, bei der der einzige rekursive Aufruf der letzte in der Funktion ist. Eine rekursive Nicht-Tail-Funktion ist eine Funktion, bei der dies nicht der Fall ist.
Eine Rückwärtsrekursion ist eine Rekursion, bei der in jedem rekursiven Aufruf der Wert des Parameters kleiner ist als im vorherigen Schritt. Eine Vorwärtsrekursion ist eine Rekursion, bei der sie mit jedem Schritt größer wird.
Dies sind zwei orthogonale Konzepte, d. H. Eine Vorwärtsrekursion kann tail-rekursiv sein oder nicht und dasselbe gilt für Rückwärtsrekursionen.
Zum Beispiel die Fakultätsfunktion oft wie dies in imperativen Sprachen geschrieben:
fac = 1
for i from 1 to n:
fac := fac * i
Die gemeinsame rekursive Version von Fakultäts zählt rückwärts (dh es nennt sich mit n-1
als Parameter), können Sie jedoch, wenn würde Wenn Sie die obige imperative Lösung direkt übersetzen, erhalten Sie eine rekursive Version, die aufwärts zählt. Es würde wie folgt aussehen:
let fac n =
let rec loop i =
if i >= n
then i
else i * loop (i+1)
in
loop 1
Dies ist eine Vorwärts-Rekursion ist und wie man sehen kann, ist es etwas umständlicher als die Rückwärts-rekursive Variante, da sie eine Hilfsfunktion erfordert. Nun ist dies nicht tail rekursiv, da der letzte Aufruf in loop
die Multiplikation ist, nicht die Rekursion. So machen es Schwanz-rekursiv, Sie so etwas tun würde:
let fac n =
let rec loop acc i =
if i >= n
then acc
else loop (i*acc) (i+1)
in
loop 1 1
Nun ist dies sowohl eine Vorwärts-Rekursion und eine Endrekursion da der rekursive Aufruf ist a) ein Tail-Call und b) nennt sich mit einem größeren Wert (i+1
).
Hier ist ein Beispiel eines Schwanzes rekursiven faktorielle Funktion:
let fac n =
let rec f n a =
match n with
0 -> a
| _ -> f (n-1) (n*a)
in
f n 1
Hier ist es nicht-tail rekursiven Gegenstück:
let rec non_tail_fac n =
match n with
0 -> 1
| _ -> (non_tail_fac n-1) * n
Der Schwanz rekursive Funktion eines Akkumulators verwendet, a, zu speichern, die Wert des Ergebnisses des vorherigen Anrufs. Dies ermöglicht es OCaml, eine Tail-Call-Optimierung durchzuführen, die dazu führt, dass der Stapel nicht überläuft. In der Regel verwendet eine rekursive Tail-Funktion einen Akkumulatorwert, um die Tail-Call-Optimierung zu ermöglichen.
Zum Beispiel kann eine rekursive Funktion build_word
, die nimmt ein char list
und kombiniert sie zu einem String heißt ['f'; 'o'; 'o']
bespannen "foo"
. Der Induktionsprozess kann auf diese Weise visualisiert werden:
build_word ['f'; 'o'; 'o']
"f"^(build_word ['o'; 'o'])
"f"^("o"^(build_word ['o']) // base case! return "o" and fold back
"f"^("o"^("o"))
"f"^("oo")
"foo"
Das war eine normale Rekursion. Beachten Sie, dass jedes Klammerpaar für einen neuen Stapelrahmen oder einen rekursiven Aufruf steht. Die Lösung für dieses Problem (d. H. "F", "fo" oder "foo") kann nicht vor dem Ende der Rekursion (wo der Basisfall erfüllt ist) abgeleitet werden. Nur dann gibt das letzte Bild das letzte Ergebnis zurück zum vorherigen, bevor es "poppt", und umgekehrt.
Theoretisch erstellt jeder Aufruf einen neuen Stapelrahmen (oder Bereich, wenn Sie so wollen), um den "Platz" für die fragmentierte Lösung zu behalten, die zurückgegeben und zu Beginn gesammelt wird.Dies kann zu stackoverflow führen (dieser Link ist eine Rekursion btw).
Ein Endrekursion Version würde wie folgt aussehen:
build_word ['f'; 'o'; 'o'] ""
build_word ['o'; 'o'], "f"
build_word ['o'] ("f"^"o")
build_word [] ("f"^"o"^"o")
"foo"
Hier wird das akkumulierte Ergebnis (oft in einem als accumulator
bekannten Variable gespeichert) geleitet vorwärts wird. Bei der Optimierung müsste Tail Call keinen neuen Stack-Frame erstellen, da die vorherigen nicht beibehalten werden müssen. Die Lösung wird "vorwärts" anstatt "rückwärts" gelöst.
Hier sind die build_word
Funktionen in zwei Versionen:
nicht-Schwanz
let build_word chars =
match chars with
| [] -> None
| [c] -> Some Char.to_string c
| hd :: tl -> build_word tl
;;
Schwanz
let build_word ?(acc = "") chars =
match chars with
| [] -> None
| [c] -> Some Char.to_string c
| hd::tl -> build_word ~acc:(acc^Char.to_string hd) tl
;;
Die Vorwärtsrekursionsarithmetikeinheiten in der akzeptierte Antwort gut erklärt von @ sepp2k.
- 1. Klasse vs Paket vs Modul vs Komponente vs Container vs Service vs Plattform in Java Welt
- 2. Opa vs Dart vs Haxe vs Coffee
- 3. Akkumulieren vs falten vs reduzieren vs komprimieren
- 4. body.scrollTop vs documentElement.scrollTop vs window.pagYOffset vs window.scrollY
- 5. ACE vs Boost vs Poco vs wxWidgets
- 6. Inline vs __inline vs __inline__ vs __forceinline?
- 7. Metaphon vs Levenshtein vs Soundex vs Hamming
- 8. Standort vs GeoPoint vs 1E6 vs Aufladen
- 9. VS 2013 MSTest vs nUnit vs xUnit
- 10. Exec vs ExecWait vs ExecShell vs nsExec :: Exec vs nsExec :: ExecToLog vs nsExec :: ExecToStack vs ExecDos vs ExeCmd
- 11. SpiderMonkey vs JavaScriptCore vs?
- 12. & vs * und | vs +
- 13. Bundler vs RVM vs Gems vs RubyGems vs Gemsets vs System Ruby
- 14. Mathematica: Unevaluated vs Aufschieben vs Halten vs Holdform vs HoldAllComplete vs etc etc
- 15. ScheduledExecutorService vs Timer vs Handler
- 16. HttpRequest vs HttpRequestMessage vs HttpRequestBase
- 17. pycuda vs theano vs pylearn2
- 18. Entfernungsabtastung vs Einzelscan vs Überspringungssuche
- 19. Htmlentities vs addslashes vs mysqli_real_escape_string
- 20. Xamarin vs Mono vs Monodevelop
- 21. Ansichtsfenster vs Fenster Vs Dokument
- 22. Redis vs Memcahced vs Hazelcast
- 23. java.lang.Void vs void vs Null
- 24. import vs __import __() vs importlib.import_module()?
- 25. QueryPerformanceCounter() vs QueryInterruptTime() vs KeQueryInterruptTime()
- 26. apc_define_constants vs hidef vs definieren
- 27. Zeiger vs auto_ptr vs shared_ptr
- 28. ASSERT vs. ATLASSERT vs. assert
- 29. Int vs NSNumber vs NSInteger
- 30. managedQuery() vs context.getContentResolver.query() vs android.provider.something.query()
Wenn ich nicht missverstanden habe, was Vorwärtsrekursion bedeutet (ich gebe zu, ich musste es googeln, wie ich den Begriff noch nie zuvor gehört hatte), ist keine Ihrer Funktionen vorwärts rekursiv. – sepp2k
Sieht so aus, als hätte ich den "Vorwärts" -Recursion-Teil der Frage übersehen. – sashang