2012-05-11 3 views
15
(take 2 (for [x (range 10) 
       :let [_ (println x)] 
       :when (even? x)] x)) 
>> (* 0 
* 1 
* 2 
* 3 
* 4 
* 5 
* 6 
* 7 
* 8 
* 9 
0 2) 

Ich nahm an, dass ich gerade bemerkenswert dicht war. Aber nein, es stellt sich heraus, dass Clojure tatsächlich die ersten 32 Elemente einer beliebigen Lazy-Sequenz auswertet (falls verfügbar). Autsch.Ist "für" nicht wirklich faul in clojure?

Ich hatte eine for mit einem rekursiven Aufruf in der :let. Ich war sehr neugierig, warum die Berechnung eher in der Breite als in der Tiefe vor sich ging. Es scheint, dass die Berechnung explodiert (obwohl, um fair zu sein, nicht die Erinnerung), während ich weiter über alle oberen Zweige des rekursiven Baums ging. Clojures 32-Chunking erzwang eine breite erste Bewertung, obwohl die logische Absicht des Codes die erste war.

Wie auch immer, gibt es einen einfachen Weg, um 1-Chunking statt 32-Chunking von Lazy-Sequenzen zu erzwingen?

Antwort

13

Michaes Fogus has written a blog entry on disabling this behavior by providing a custom ISeq implementation.

Um schamlos aus the modified version by Colin Jones zu stehlen:

(defn seq1 [#^clojure.lang.ISeq s] 
    (reify clojure.lang.ISeq 
    (first [_] (.first s)) 
    (more [_] (seq1 (.more s))) 
    (next [_] (let [sn (.next s)] (and sn (seq1 sn)))) 
    (seq [_] (let [ss (.seq s)] (and ss (seq1 ss)))) 
    (count [_] (.count s)) 
    (cons [_ o] (.cons s o)) 
    (empty [_] (.empty s)) 
    (equiv [_ o] (.equiv s o)))) 

Ein einfacherer Ansatz in The Joy of Clojure gegeben:

(defn seq1 [s] 
    (lazy-seq 
    (when-let [[x] (seq s)] 
     (cons x (seq1 (rest s)))))) 
+0

Vielen Dank! Das hat mich für zwei Stunden ratlos gemacht. Ich konnte nicht herausfinden, warum (nimm 1 (map some-func [1 2 3 4]))) wertet einige Funktionen für alle 4 Elemente aus ... Ich muss sagen, es war nicht offensichtlich, warum das geschah, vom Lesen der Dokumente auf "map" und "take" –

3

die Frage im Titel zu beantworten, nein, das ist for nicht faul. However es:

nimmt einen Vektor von einem oder mehreren Bindungs ​​ -form/sammel expr Paare, jeweils gefolgt von null oder mehr Modifikatoren und ergibt eine lazy Sequenz von Auswertungen der expr.

(Hervorhebung von mir)

So what's going on?

grundsätzlich Clojure immer streng bewertet. Lazy Seqs verwenden im Grunde die gleichen Tricks wie Python mit ihren Generatoren usw. Strenge Auszüge in faulen Klamotten.

Mit anderen Worten, foreifrig gibt eine faul Sequenz. Das wird nicht bewertet, bis Sie danach fragen, und wird chunked werden.