Hier ist eine kleine Tabelle, die nützlich ist, wenn Sie Code schreiben, der eine Sequenz mithilfe der "profanen" Rekursion (mithilfe des Stapels) oder mithilfe recur(mithilfe der schwanzrekursiven Optimierung, also der eigentlichen Schleife) durchläuft .

Beachten Sie die Unterschiede im Verhalten von restund next. In Kombination seqdamit ergibt sich die folgende Redewendung, bei der das Ende der Liste über seqund der Rest der Liste über rest(angepasst aus "The Joy of Clojure") getestet wird :
(defn print-seq [s]
(when (seq s)
(assert (and (not (nil? s)) (empty? s)))
(prn (first s))
(recur (rest s))))
Warum ist nexteifriger als rest?
Wenn (next coll)ausgewertet wird, kann das Ergebnis sein nil. Dies muss sofort bekannt sein (dh nilmuss tatsächlich zurückgegeben werden), da der Anrufer basierend auf der Wahrheit von verzweigen kann nil.
Wenn (rest coll)ausgewertet wird, kann das Ergebnis nicht sein nil. Wenn der Aufrufer das Ergebnis dann nicht mithilfe eines Funktionsaufrufs auf Leere testet, kann die Erzeugung eines "nächsten Elements" in einer Lazy-Seq auf die tatsächlich benötigte Zeit verzögert werden.
Beispiel
Eine völlig faule Sammlung, alle Berechnungen werden "bis zur Verwendung angehalten".
(def x
(lazy-seq
(println "first lazy-seq evaluated")
(cons 1
(lazy-seq
(println "second lazy-seq evaluated")
(cons 2
(lazy-seq
(println "third lazy-seq evaluated")))))))
Die Berechnung von "x" wird nun beim ersten "Lazy-Seq" ausgesetzt.
Mit dem eifrigen nächsten sehen wir zwei Bewertungen:
(def y (next x))
(type y)
(first y)
- Die erste Lazy-Seq wird ausgewertet, was zum Ausdruck führt
first lazy-seq evaluated
- Dies führt zu einer nicht leeren Struktur: einem Nachteil mit 1 links und einem Lazy-Seq rechts.
nextmuss möglicherweise zurückkehren, nilwenn der rechte Zweig leer ist. Wir müssen also eine Ebene tiefer prüfen.
- Die zweite Lazy-Seq wird ausgewertet, was zum Ausdruck führt
second lazy-seq evaluated
- Dies führt zu einer nicht leeren Struktur: einem Nachteil mit 2 links und einem Lazy-Seq rechts.
- Also nicht zurückkehren
nil, sondern die Nachteile zurückgeben.
- Wenn Sie das
firstvon erhalten y, gibt es nichts zu tun, außer 2 von den bereits erhaltenen Nachteilen abzurufen.
Bei Verwendung des Lazer sehen restwir eine Bewertung (beachten Sie, dass Sie xzuerst neu definieren müssen , damit dies funktioniert).
(def y (rest x))
(type y)
(first y)
- Die erste Lazy-Seq wird ausgewertet, was zum Ausdruck führt
first lazy-seq evaluated
- Dies führt zu einer nicht leeren Struktur: einem Nachteil mit 1 links und einem Lazy-Seq rechts.
restkehrt niemals zurück nil, selbst wenn die Lazy-Seq auf der rechten Seite die leere Seq ergibt.
- Wenn der Anrufer mehr wissen muss (ist die Sequenz leer?), Kann er später den entsprechenden Test für die Lazy-Sequenz durchführen.
- Also sind wir fertig, geben Sie einfach die Lazy-Seq als Ergebnis zurück.
- Beim Erhalten des
firstvon ymuss das Lazy-Seq einen Schritt weiter ausgewertet werden, um das 2 zu erhalten
Seitenleiste
Beachten Sie, dass yder Typ ist LazySeq. Dies mag offensichtlich erscheinen, ist aber LazySeqnicht "Sache der Sprache", sondern "Sache der Laufzeit", die kein Ergebnis, sondern einen Berechnungszustand darstellt. In der Tat (type y)ist clojure.lang.LazySeqbedeutet nur , „wir wissen nicht , den Typ noch, Sie mehr zu erfahren zu tun haben“. Immer wenn eine Clojure-Funktion wie nil?etwas trifft, das einen Typ hat clojure.lang.LazySeq, wird eine Berechnung durchgeführt!
PS
In Joy of Clojure, 2. Auflage, auf Seite 126, wird anhand eines Beispiels iterateder Unterschied zwischen nextund veranschaulicht rest.
(doc iterate)
Wie sich herausstellt, funktioniert das Beispiel nicht. In diesem Fall gibt es tatsächlich keinen Unterschied im Verhalten zwischen nextund rest. Ich nextweiß nicht warum, weiß vielleicht, dass es niemals nilhierher zurückkehren wird und verwendet standardmäßig das Verhalten von rest.
(defn print-then-inc [x] (do (print "[" x "]") (inc x)))
(def very-lazy (iterate print-then-inc 1))
(def very-lazy (rest(rest(rest(iterate print-then-inc 1)))))
(first very-lazy)
(def less-lazy (next(next(next(iterate print-then-inc 1)))))
(first less-lazy)
()wahr undnilfalsch ist. Also(defn keep-going [my-coll] (if (rest my-coll) (keep-going (rest my-coll)) "Finished.")wird es für immer weitergehen - oder besser gesagt, der Stapel wird überlaufen - während(defn keep-going [my-coll] (if (next my-coll) (keep-going (next my-coll)) "Finished.")es fertig ist. (OP hat dies sicherlich inzwischen herausgefunden; ich füge diese Bemerkung für andere hinzu.)