2009-06-28 33 views
3

當我嘗試向Clojure中的無限延遲序列添加元數據時,出現堆棧溢出,並且如果我取消了元數據,那麼它工作得很好。爲什麼添加with-meta宏打破懶惰seq?將元數據添加到延遲序列

首先創建一個非常漂亮的數量無限序列:

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

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

然後,添加一些元數據到每個懶-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] 

嘗試將元數據了一個級別:

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

下面是有限序列上的元數據示例:

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

user> (also-works) 
(4) 
user> (meta (rest (also-works))) 
{:a 5} 
user> 
+0

lazy-seq和with-meta都是宏,所以你應該能夠通過macroexpand和resp獲得進一步的瞭解。 macroexpand-1。 – Svante 2009-06-29 00:26:02

回答

6

因爲LazySeq只要你在LazySeq調用withMeta評估其身體。你失去了你的懶惰。

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

seq()評估懶序列的身體,如果它尚未評估。你上面的代碼在連續的惰性序列上連續調用with-meta,這會對它們進行評估直到堆棧爆炸。我不認爲現在有任何方法可以將元數據添加到惰性seq而不會導致它評估其正文。

+0

FWIW,[CLJ-1800](https://dev.clojure.org/jira/browse/CLJ-1800)中的第二個補丁,如果被接受,將提供一種方式。 – 2018-01-20 21:46:37