Tail-rekursive Funktion zu finden, die Tiefe des Baums in Ocaml
Habe ich eine Art tree
wie folgt definiert
type 'a tree = Leaf of 'a | Node of 'a * 'a tree * 'a tree ;;
Habe ich eine Funktion zu finden, die Tiefe des Baumes wie folgt
let rec depth = function
| Leaf x -> 0
| Node(_,left,right) -> 1 + (max (depth left) (depth right))
;;
Diese Funktion ist nicht tail-rekursiv. Gibt es eine Möglichkeit für mich, dies zu schreiben-Funktion in tail-rekursive Weise?
- Ich glaube, Sie können, wenn Sie transformieren auf eine continuation-passing style.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Können Sie trivial tun dies durch drehen wird die Funktion in CPS (Continuation-Passing-Style). Die Idee ist, dass anstelle der Berufung
depth left
, und dann Dinge computing basierend auf diesem Ergebnis, Sie nennendepth left (fun dleft -> ...)
, wobei das zweite argument ist "was zu berechnen, sobald das Ergebnis (dleft
) verfügbar ist".Dies ist ein bekannter trick, dass kann jede Funktion tail-rekursiv. Voilà, es ist tail-rec.
Den nächsten bekannten trick in der Tasche ist "defunctionalize" die CPS-Ergebnis. Die Darstellung der Fortsetzungen (die
(fun dleft -> ...)
Teile) als Funktionen ist ordentlich, aber möchten Sie vielleicht, um zu sehen, was es sieht aus wie data. Also ersetzen wir jedes dieser Verschlüsse durch eine konkrete Konstruktor eines Datentyps, wobei die freien Variablen in der it.Wir haben hier drei Fortsetzung Verschlüsse:
(fun dleft -> depth right (fun dright -> k ...))
, die nur verwendet die Umgebungsvariablenright
undk
,(fun dright -> ...)
, die wiederverwendetk
und die jetzt verfügbaren Links führendleft
, und(fun d -> d)
die anfängliche Berechnung, dass nicht alles erfassen.Den defunctorized Funktion sieht wie folgt aus:
Anstatt eine Funktion
k
und Ihre Anwendung auf die Blätter (k 0
), Baue ich eine Daten-Typ('a, int) cont
werden muss spätereval
uated zu berechnen, ein Ergebnis.eval
, wenn es übergeben bekommt eineKleft
, das tut, was die Schließung(fun dleft -> ...)
war zu tun, das ist es rekursiv aufrufendepth
auf den rechten Teilbaum.eval
unddepth
sind wechselseitig rekursiv.Schaue jetzt hart an der
('a, 'b) cont
, was ist dieser Datentyp? Es ist eine Liste!Sowie eine Liste ist ein stack. Was wir hier haben, ist eigentlich eine Materialisierung (transformation in Daten) der call stack des vorherigen rekursiven Funktion, mit der zwei verschiedene Fälle, entsprechend den beiden verschiedenen Arten von nicht-tailrec Anrufe.
Beachten Sie, dass die defunctionalization ist nur zum Vergnügen da. In der Praxis werden die CPS-version ist kurz, leicht zu leiten, die von hand eher leicht zu Lesen, und ich würde empfehlen, es zu benutzen. Verschlüsse muss zugewiesen werden, im Speicher, aber so sind die Elemente der
('a, 'b) cont
- wenn auch diejenigen, die vielleicht vertreten werden mehr kompakt`. Ich würde stick auf die CPS-version, es sei denn, es gibt sehr gute Gründe, etwas komplizierter machen.length : 'a list -> int
- Funktion, werden Sie feststellen, dass die resultierendecont
- Typ ist isomorph zu ganzen zahlen, und mit Ganzzahlen statt direkt gibt Sie die Konstanten-Speicher tailrec version.In diesem Fall (Tiefe Berechnung), Sie können sich im Laufe der Paare (
subtree depth
*subtree content
) erhalten Sie die folgenden tail-rekursive Funktion:Weitere Allgemeine Fälle, werden Sie in der Tat müssen Sie mit dem CPS-transformation beschrieben durch Gabriel.
depth
ist eine versteckte state-Monade verwendet wird, zu sammeln Informationen über die Ergebnis-ein Hinweis würde die Aufgabe auch erledigen.Es gibt eine ordentliche und eine generische Lösung, die mit
fold_tree
- und CPS - continuous-passing style: