So konvertieren Sie in Clojure eine faule Sequenz in eine nicht faule

95

Ich habe in Clojure Folgendes versucht und erwartet, dass die Klasse einer nicht faulen Sequenz zurückgegeben wird:

(.getClass (doall (take 3 (repeatedly rand))))

Dies kehrt jedoch immer noch zurück clojure.lang.LazySeq. Ich vermute, dass doalldie gesamte Sequenz ausgewertet wird, aber die ursprüngliche Sequenz zurückgegeben wird, da sie für das Auswendiglernen immer noch nützlich ist.

Was ist also das idiomatische Mittel, um aus einer faulen eine nicht faule Sequenz zu erstellen?

Tim Clemons
quelle
Ich bin überrascht, dass niemand gefragt hat, warum Sie über die tatsächliche Art des Rückgabewerts vondoall
tar
Sie könnten in einen Vektor konvertieren:(vec (take 3 (repeatedly rand)))
Kris

Antworten:

161

doallist alles, was du brauchst. Nur weil der seqTyp has hat, LazySeqheißt das nicht, dass eine Auswertung ansteht. Lazy seqs speichert ihre Ergebnisse im Cache. Alles, was Sie tun müssen, ist, die Faulen seqeinmal (wie doallauch) zu gehen, um alles zu erzwingen und sie somit nicht faul zu machen. seqerzwingt nicht die Auswertung der gesamten Sammlung.

Rich Hickey
quelle
2
Ich habe dies in die akzeptierte Antwort geändert. Auf welche Weise können Sie feststellen, ob ein LazySeq zuvor evaluiert wurde?
Tim Clemons
10
Ich glaube du rufst einfach an realized?.
toofarsideways
1
Es sollte wahrscheinlich eine realizepassende Operation geben realized?.
Reut Sharabani
Das ist alles sehr schön. Da es einigen Funktionen wie contains?egal ist, ob Sie die Lazy Seq erkannt haben oder nicht, beantwortet dies die spezifische Frage wie gestellt, aber weniger den Titel der Frage.
Matanster
75

Dies ist bis zu einem gewissen Grad eine Frage der Taxonomie. Eine faule Sequenz ist nur eine Art von Sequenz, ebenso wie eine Liste, ein Vektor oder eine Karte. Die Antwort lautet natürlich: "Es hängt davon ab, welche Art von nicht fauler Sequenz Sie erhalten möchten:
Treffen Sie Ihre Wahl aus:

  • eine ex-faule (vollständig ausgewertete) faule Sequenz (doall ... )
  • eine Liste für den sequentiellen Zugriff (apply list (my-lazy-seq)) OR (into () ...)
  • ein Vektor für den späteren Direktzugriff (vec (my-lazy-seq))
  • eine Karte oder ein Set, wenn Sie einen bestimmten Zweck haben.

Sie können jede Art von Sequenz wählen, die Ihren Anforderungen am besten entspricht.

Arthur Ulfeldt
quelle
Dies ist die beste Antwort.
Felipe
4
Die akzeptierte Antwort ist technisch korrekt, aber diese Antwort war für mich am nützlichsten. Ich habe versucht, eine Funktion über einen Vektor abzubilden und dann die Ergebnisse in eine Datei zu spucken, und selbst nach dem Aufruf von doall enthielt die Datei "clojure.lang.LazySeq@address" anstelle des Inhalts der Sequenz. Das Aufrufen von vec auf der zurückgegebenen Wertekarte brachte mir das, was ich brauchte, um in die Datei zu spucken.
Jesse Rosalia
1
@ JesseRosalia Es ist gut zu wissen, dass die einzige Antwort von Rich Hickey in ganz SO technisch korrekt war. ;-)
Phil Cooper
(vec (my-lazy-seq))ist nicht so schön in Situationen wie den folgenden: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]Da cheshirebeschließt, eine Lazy-Seq von(json/parse-string)
Codeasone
Mitigation für die oben war begierig darauf , zu verwenden(json/parse-string-strict)
codeasone
22

Dieser reiche Typ scheint seine Clojure zu kennen und hat absolut Recht.
Aber ich denke, dieses Code-Snippet könnte anhand Ihres Beispiels eine nützliche Ergänzung zu dieser Frage sein:

=> (realized? (take 3 (repeatedly rand))) 
false
=> (realized? (doall (take 3 (repeatedly rand)))) 
true

In der Tat hat sich der Typ nicht geändert, aber die Verwirklichung hat sich geändert

Peter
quelle
2
Es ist jedoch erwähnenswert, dass Sie nicht die gesamte Sequenz erzwingen müssen, um realized?zurückzukehren true. ZB(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry
22
Dieser reiche Kerl: D haha
nimrod
10
@nimrod :) das Wortspiel sollte jedoch in "hís clojure" sein.
Peter
10
Für diejenigen, die es nicht wissen, hat "der Reiche" Clojure erfunden.
Erturne
1
@AlexCoventry Ihr Beispiel gibt zurück[true true]
7

Ich bin auf diesen Blog- Beitrag gestoßen, doallweil ich nicht rekursiv bin. Dafür fand ich, dass der erste Kommentar im Beitrag den Trick machte. Etwas in der Art von:

(use 'closure.walk)
(postwalk identity nested-lazy-thing)

Ich fand dies nützlich in einem Komponententest, bei dem ich die Auswertung einiger verschachtelter Anwendungen maperzwingen wollte , um eine Fehlerbedingung zu erzwingen.

Leeor
quelle
5
(.getClass (into '() (take 3 (repeatedly rand))))
stupito
quelle
3
Das ist eine schreckliche Idee. Es kehrt die Eingangssequenz um.
Amalloy
3
In diesem Fall macht das Umkehren der Eingabe natürlich keinen Unterschied, da es sich nur um 3 Zufallszahlen handelt .... :-)
mikera