4

当我使用lazySeq 时有很多困惑。

问题:

(def fib
  (lazy-seq
    (concat [0 1] (map + fib (rest fib))))) ;; It's Ok
(take 10 fib) ;; Bomb

收到错误消息:StackOverflowError 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

concatmap返回惰性序列,为什么上面的程序长得像但又区别开来?

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

4

1 回答 1

3

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

您需要使用不会立即在 fib 上调用 next 的东西,例如drop

(def fib
  (lazy-seq
   (concat [0 1] (map + fib (drop 1 fib)))))

此外,在其他情况下 where is lazy-seqinside不会被执行,因为它被包装在一个操作中,这使得整个表达式成为将来请求下一个 ISeq 时调用的函数。concatrestlazy-seq

希望这可以解决问题。

于 2013-04-30T09:41:06.447 回答