Domanda

Quando provo ad aggiungere metadati a un'infinita sequenza pigra in Clojure, ottengo un overflow dello stack e se tolgo i metadati, allora funziona perfettamente. Perché l'aggiunta della with-meta macro interrompe il pigro seq?

Per prima cosa crea un seq infinito di un numero molto bello:

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

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

Quindi, aggiungi alcuni metadati a ciascuna delle istanze 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]

Prova a spostare i metadati di un livello superiore:

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

Ecco un esempio di metadati su una sequenza finita:

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

user> (also-works)
(4)
user> (meta (rest (also-works)))
{:a 5}
user> 
È stato utile?

Soluzione

Perché un LazySeq valuta il suo corpo non appena chiami withMeta sul seq(). Perdi la tua pigrizia.

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

with-meta valuta il corpo del seq pigro se non è già stato valutato. Il tuo codice sopra continua a chiamare <=> su sequenze pigre successive, che le valuta tutte fino allo scoppio dello stack. Non credo ci sia attualmente alcun modo per aggiungere metadati a un seq pigro senza indurlo a valutare il suo corpo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top