Pregunta

Cuando intento agregar metadatos a una secuencia perezosa infinita en Clojure, obtengo un desbordamiento de pila, y si elimino los metadatos, entonces funciona bien. ¿Por qué agregar la macro with-meta rompe la secuencia lenta?

Primero crea una secuencia infinita de un número muy agradable:

(defn good []
  (lazy-seq 
    (cons 42
      (good))))

user> (take 5 (good))
(42 42 42 42 42)

Luego, agregue algunos metadatos a cada una de las instancias de lazy-seq:

(defn bad []
  (lazy-seq 
    (cons 42
      (with-meta 
       (bad)
       {:padding 4}))))


user> (take 5 (bad))
java.lang.StackOverflowError (NO_SOURCE_FILE:0)
  [Thrown class clojure.lang.Compiler$CompilerException]

Intente subir los metadatos un nivel:

(defn also-bad []
  (with-meta 
   (lazy-seq 
     (cons 42
       (also-bad)))
   {:padding 4}))

user> (take 5 (foo))
java.lang.StackOverflowError (NO_SOURCE_FILE:0)
  [Thrown class clojure.lang.Compiler$CompilerException]

Aquí hay un ejemplo de metadatos en una secuencia finita:

(defn also-works []
     (lazy-seq 
       (cons 4 
         (with-meta 
          () 
          {:a 5}))))

user> (also-works)
(4)
user> (meta (rest (also-works)))
{:a 5}
user> 
¿Fue útil?

Solución

Porque un LazySeq evalúa su cuerpo tan pronto como llame a withMeta en el seq(). Pierdes tu pereza.

public final class LazySeq extends Obj implements ISeq, List{
    ...
    public Obj withMeta(IPersistentMap meta){
        return new LazySeq(meta, seq());
    }
    ...
}

with-meta evalúa el cuerpo de la secuencia lenta si aún no se ha evaluado. Su código anterior sigue llamando a <=> en sucesivas secuencias perezosas, que las evalúa todas hasta que explota la pila. No creo que haya alguna forma de agregar metadatos a una secuencia perezosa sin hacer que evalúe su cuerpo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top