Clojure 如何正确使用LazySeq

Clojure 如何正确使用LazySeq,clojure,lazy-sequences,Clojure,Lazy Sequences,当我使用lazySeq时,会有很多困惑 问题: (def fib (lazy-seq (concat [0 1] (map + fib (rest fib))))) ;; It's Ok (take 10 fib) ;; Bomb 获取错误消息:stackoverflowerrror clojure.lang.RT.more(RT.java:589) 以下解决方案效果良好: (def fib (concat [0 1] (lazy-seq (map + fib (rest fi

当我使用lazySeq时,会有很多困惑

问题:

(def fib
  (lazy-seq
    (concat [0 1] (map + fib (rest fib))))) ;; It's Ok
(take 10 fib) ;; Bomb
获取错误消息:stackoverflowerrror clojure.lang.RT.more(RT.java:589)

以下解决方案效果良好:

(def fib
  (concat [0 1] (lazy-seq (map + fib (rest fib))))) ;; Works well

(def fib
  (lazy-cat [0 1] (map + fib (rest fib)))) ;; Works well
concat
map
都返回惰性序列,为什么上面的程序看起来很像,但有区别


更详细地说,为什么第一个示例(
lazy-seq
包装
concat
)失败,而下面的示例(
lazy-seq
包装
map
)成功?

问题是在map操作中使用
rest
。基本上,当您的lazy seq将调用表达式:
(concat[01](map+fib(rest-fib))
返回ISeq对象时,将发生对fib的
rest
调用(因为这是
map
的一个参数,必须先执行它,然后传递给map,map是lazy的,但在我们到达lazyness之前调用rest)
rest
将尝试调用fib,这是一个
LazySeq
对象,更多的将导致fib-LazySeq获得下一个ISeq,该ISeq再次涉及执行整个
concat
操作,该操作涉及
rest
,它将继续以这种方式进行,直到堆栈被吹走

您需要使用一些不会立即调用fib中的next的东西,比如
drop

(def fib
  (lazy-seq
   (concat [0 1] (map + fib (drop 1 fib)))))
另外,在另一种情况下,
lazy-seq
位于
concat
内部,
rest
不会执行,因为它被包装在
lazy-seq
操作中,该操作使整个表达式成为将来请求下一个ISeq时调用的函数


希望这能澄清问题。

可能重复@DominicKexel,实际上不一样,我的问题是关注
惰性seq的顺序(或位置)
。在第一个示例中,
lazy-seq
定位在
concat
之外,并使用bomb-In,但第二个
lazy-seq
仅包装
映射,效果良好。所以我认为
lazy seq
的顺序很重要,这就是我想知道的,你的链接没有解释它。我很确定这个问题的答案解释得很好。mikera(谁的答案被接受)说“你把它们放在哪个顺序并不重要。”。但我的问题表明顺序是重要的。你认为答案如何解释?我读了问题和答案,注意到这不是我想要的,你有没有读清楚我的问题@DominicKexel@mikera期待你的回答。哇!就这样!我输入了
(take 10(rest(iterate inc 1))
,代码运行正常,所以我认为
rest
或多或少是“懒惰的”,但是
rest
的文档没有说它返回
懒惰序列。我认为这是最令人困惑的事情。ISeq(clojure中懒惰的基础)与IEnumerable(在CLR中)或迭代器(在Java中)不同。在这些情况下,next方法返回next对象,因此它们必须保持关于当前位置的状态,而ISeq next方法返回ISeq(不是对象),因此它不需要任何状态。就像迭代器是:
[1234]
,ISeq是
[1[2[3[4nil]]]]
。这种差异有时是造成混淆的原因