In Scala 2.8.x wurde eine neue Annotation ( @tailrec
) hinzugefügt, die einen Kompilierungsfehler ergibt, wenn der Compiler keine Tail-Call-Optimierung für die mit Annotationen versehene Methode durchführen kann.
Gibt es eine ähnliche Einrichtung in Clojure in Bezug auf loop/recur
?
BEARBEITEN: Nachdem ich die erste Antwort auf meine Frage (Danke, Bozhidar Batsov) gelesen und weiter in den Clojure-Dokumenten gesucht hatte, stieß ich auf folgendes:
(Wiederholungsexpres *)
Analysiert die Ausdrücke in der Reihenfolge und bindet dann parallel die Bindungen des Rekursionspunkts an die Werte der Ausdrücke. Wenn der Rekursionspunkt eine Fn-Methode war, werden die Params erneut eingebunden. Wenn der Rekursionspunkt eine Schleife ist, bindet er die Schleifenbindungen erneut. Die Ausführung springt dann zum Rekursionspunkt zurück. Der rekursive Ausdruck muss genau mit der Genauigkeit des Rekursionspunkts übereinstimmen. Insbesondere, wenn der Rekursionspunkt die Spitze einer variadischen fn-Methode war, gibt es keine Sammlung von Restargumenten - eine einzelne seq (oder null) sollte übergeben werden. Wiederholung in einer anderen als eine Endposition ist ein Fehler .
Beachten Sie, dass recur das einzige nicht stapelaufbauende Schleifenkonstrukt in Clojure ist. Es gibt keine Tail-Call-Optimierung und die Verwendung von Self-Calls für das Schleifen von unbekannten Grenzen wird abgeraten. recur ist funktional und seine Verwendung in der Endposition wird vom Compiler überprüft [emphasis is mine].
%Vor%Es gibt keine Tail-Call-Optimierung, wenn Sie Schleife / Wiederholung AFAIK verwenden. Ein Zitat aus der offiziellen Dokumentation:
In Abwesenheit von veränderlichen lokalen Variablen, Schleifen und Iterationen müssen nehmen Sie eine andere Form als in Sprachen mit eingebauten für oder während Konstrukte, die von gesteuert werden Zustand ändern. In funktionalen Sprachen Looping und Iteration sind ersetzt / implementiert über rekursiv Funktionsaufrufe. Viele solche Sprachen garantieren, dass Funktionsaufrufe getätigt werden Heckposition nicht Stack verbrauchen Raum und damit rekursive Schleifen nutze konstanten Raum. Seit Clojure verwendet die Java-Aufrufkonventionen, es kann und macht nicht dasselbe Tail Call-Optimierung garantiert. Stattdessen bietet es das wiederkehrende Spezial Operator, der Konstantenraum ausführt Rekursives Looping durch Rebinding und zur nächsten umschließenden Schleife springen oder Funktionsrahmen. Während nicht als allgemein als Tail-Call-Optimierung, es lässt die meisten gleich elegant zu Konstruiert und bietet den Vorteil zu überprüfen, dass Aufrufe wiederkehren können nur in einer Schwanzposition passieren.
Eigentlich ist die Situation in Scala w.r.t. Tail Call Optimization ist die gleiche wie in Clojure : Es ist möglich, es in einfachen Situationen wie Selbstrekursion durchzuführen, aber nicht in allgemeinen Situationen wie dem Aufruf einer beliebigen Funktion in der Endposition.
Dies liegt an der Funktionsweise der JVM - damit TCO auf der JVM arbeiten kann, müsste die JVM selbst dies unterstützen, was sie derzeit nicht tut (obwohl sich dies möglicherweise ändern könnte) JDK7 wird veröffentlicht).
Siehe z.B. diesen Blogeintrag für eine Diskussion über TCO und Trampolinspringen in Scala. Clojure hat genau die gleichen Eigenschaften, um nicht-Stack-konsumierende (= Tail-Call-optimierte) Rekursion zu ermöglichen; Dies umfasst das Auslösen eines Kompilierungsfehlers, wenn der Benutzercode versucht, recur
in der Nicht-Endposition aufzurufen.
Tags und Links clojure tail-recursion