iCo*_*ime 4 clojure lazy-evaluation
在定义无限序列时,我注意到缺点是避免无限递归.但是,我不明白的是为什么.这是有问题的代码:
(defn even-numbers
([] (even-numbers 0))
([n] (cons n (lazy-seq (even-numbers (+ 2 n))))))
(take 10 (even-numbers))
;; (0 2 4 6 8 10 12 14 16 18)
Run Code Online (Sandbox Code Playgroud)
这很好用; 但由于我喜欢质疑,我开始想知道为什么需要利弊(除了包括0).毕竟,lazy-seq函数创建了一个lazy-seq.这意味着,在调用(或分块)之前,不应计算其余值.所以,我试过了.
(defn even-numbers-v2
([] (even-numbers-v2 0))
([n] (lazy-seq (even-numbers-v2 (+ 2 n)))))
(take 10 (even-numbers-v2))
;; Infinite loooooooooop
Run Code Online (Sandbox Code Playgroud)
所以,现在我知道缺点是必要的,但是我想知道为什么有必要对所谓的懒惰序列进行惰性评估
延迟seqs是一种推迟实际seq元素计算的方法,但最终需要计算这些元素.实际上并不需要涉及cons- 例如clojure.core/concat在处理分块操作数时使用"chunked conses",并且可以将任何具体的seq类型包装在其中lazy-seq- 但是lazy-seq如果有任何层,则需要进行某种非惰性返回seq处理将发生.否则甚至不会有第一个要素.
把自己放在一个懒惰的seq函数的位置.实际上,调用者已经告诉它"这里的所有意图和目的都是seq,但我不想在以后计算实际元素".现在我们的函数需要一些实际的元素来操作,所以它戳戳并刺激seq试图让它产生一些元素......然后是什么?
如果剥离某些lazy-seq图层最终会产生一个Cons单元格,一个列表,一个矢量上的seq或任何其他具有实际元素的具体seq类似的东西,那么很好,该函数可以从中读取一个元素并取得进展.
但是,如果剥离这些层的唯一结果是更多层被揭示,并且它lazy-seq一直向下,那么......没有任何元素可以找到.并且由于原则上无法确定是否通过剥离足够多的层最终会产生一些元素(参见暂停问题),消耗这种不可实现的懒惰seq的函数通常别无选择,只能继续循环永远.
从另一个角度来看,让我们考虑你的even-numbers-v2功能.它接受一个参数并返回一个lazy-seq包含对自身的进一步调用的对象.现在,它接收的原始参数(n)用于计算递归调用((+ 2 n))的参数,但是否则不会放在任何数据结构中或以其他方式传递给调用者,因此没有理由将其作为由此产生的seq的元素.所有调用者都看到该函数已经生成了一个懒惰的seq对象,它只能在搜索序列的实际元素时展开它; 当然,情况会重演(在这种情况下并非严格地说,但这只是因为+在处理多头时最终会抱怨算术溢出).
| 归档时间: |
|
| 查看次数: |
132 次 |
| 最近记录: |