2012-05-11 11 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) 

Supuse que estaba siendo extraordinariamente densa. Pero no, resulta que Clojure realmente evalúa los primeros 32 elementos de cualquier secuencia floja (si está disponible). Ay.¿Es 'para' no realmente perezoso en clojure?

Tuve un for con una llamada recursiva en el :let. Tenía mucha curiosidad por saber por qué el cálculo parecía avanzar primero en lugar de profundizar primero. Parece que el cálculo (aunque, para ser justos, no la memoria) estaba explotando a medida que avanzaba por todas las ramas superiores del árbol recursivo. El 32-fragmentación de Clojure estaba forzando la primera evaluación de amplitud, aunque la intención lógica del código era la profundidad primero.

De todos modos, ¿hay alguna manera simple de forzar 1-fragmentación en lugar de 32-fragmentación de secuencias perezosas?

Respuesta

13

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

para robar descaradamente de the modified version by Colin Jones:

(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)))) 

Un enfoque más sencillo se da in The Joy of Clojure:

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

¡Gracias! Esto me tuvo perplejo por dos horas. No pude entender por qué (tomar 1 (mapa alguno-func [1 2 3 4])) evalúa algún func para los 4 elementos ... Debo decir que no era obvio por qué sucedía eso, de leer los documentos en "mapa" y "tomar" –

3

Para responder a la pregunta en su título, no, for no es perezoso. However, que:

Toma un vector de pares de uno o más -forma de unión/de recogida de expr, cada una seguida de cero o más modificadores, y produce una secuencia perezoso de evaluaciones de expr.

(el énfasis es mío)

Así what's going on?

básicamente Clojure siempre evalúa estrictamente. Lazy seqs básicamente utiliza los mismos trucos que python con sus generadores, etc. Estrictas evaluaciones en ropa perezosa.

En otras palabras, foransiosamente devuelve un vago secuencia. Que no será evaluado hasta que lo solicite, y será fragmentado.