Question

Lorsque j'essaie d'ajouter des métadonnées à une séquence infinie de lazy dans Clojure, la pile déborde, et si je supprime les métadonnées, cela fonctionne parfaitement. Pourquoi l’ajout de la macro with-meta interrompt-il la séquence paresseuse?

Créez d’abord une séquence infinie d’un très beau nombre:

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

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

Ajoutez ensuite des métadonnées à chacune des instances 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]

Essayez de déplacer les métadonnées d'un niveau supérieur:

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

Voici un exemple de métadonnées sur une séquence finie:

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

user> (also-works)
(4)
user> (meta (rest (also-works)))
{:a 5}
user> 
Était-ce utile?

La solution

Parce qu'un LazySeq évalue son corps dès que vous appelez withMeta sur le seq(). Vous perdez votre paresse.

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

with-meta évalue le corps du seq différé s'il n'a pas déjà été évalué. Votre code ci-dessus continue d’appeler <=> sur des séquences successives, qui les évaluent toutes jusqu’à ce que la pile explose. Je ne pense pas qu’il soit actuellement possible d’ajouter des métadonnées à un seq paresseux sans le forcer à évaluer son corps.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top