我很难理解懒惰。
有人可以帮助我理解为什么我的下面的功能不是懒惰的
(defn my-red
([f coll] (my-red f (first coll) (rest coll) ))
([f init coll]
(let [mr (fn [g i c d]
(if (empty? c) d
(recur g (g i (first c)) (rest c) (conj d (g i (first c)) ))))]
(lazy-seq (mr f init coll [])))))
而在clojure.org/lazy上给出的这个例子是
(defn filter
"Returns a lazy sequence of the items in coll for which
(pred item) returns true. pred must be free of side-effects."
[pred coll]
(let [step (fn [p c]
(when-let [s (seq c)]
(if (p (first s))
(cons (first s) (filter p (rest s)))
(recur p (rest s)))))]
(lazy-seq (step pred coll))))
答案 0 :(得分:8)
filter
中的懒惰来自对递归循环的filter
分支中if
的调用。这就是代码命中另一个lazy-seq
并停止评估seq直到请求另一个元素。
my-red
的实施只能呼叫lazy-seq
一次,这意味着你的seq根本没有被评估或被完全评估。
答案 1 :(得分:2)
mr函数将重复整个coll。也许你的缩进误导了你。正确缩进,并删除了一些无用的参数,该函数看起来像这样:
(defn my-red
([f coll] (my-red f (first coll) (rest coll) ))
([f init coll]
(let [mr (fn [i c d]
(if (empty? c)
d
(recur (f i (first c))
(rest c)
(conj d (f i (first c))))))]
(lazy-seq (mr init coll [])))))
基本上,你在mr函数周围包装(lazy-seq),它在一个大的recur循环中完成所有工作。
答案 2 :(得分:2)
所有lazy-seq
都会采用其参数并延迟执行它。为了产生真正的延迟序列,每个链接必须包含在lazy-seq调用中。懒惰的“粒度”是对lazy-seq
的调用之间的工作量。解决这个问题的一种方法是使用返回惰性seq的更高级函数。
此外,尾递归和懒惰是互斥的。这不会导致堆栈溢出,因为在每一步,递归调用都会被包装到一个惰性seq中并返回。如果调用者然后尝试计算延迟seq,则调用递归调用,但序列函数的原始调用者调用它,而不是序列函数本身,这意味着堆栈不会增长。这有点类似于通过trampolines实现优化尾递归的想法(参见Clojure的trampoline
函数)。
这是一个懒惰的版本:
(defn my-red
([f coll] (my-red f (first coll) (rest coll) ))
([f init coll]
(let [mr (fn mr [g i c]
(if (empty? c)
nil
(let [gi (g i (first c))]
(lazy-seq (cons gi (mr g gi (rest c)))))))]
(lazy-seq (mr f init coll)))))
注意mr
的每次运行如何立即返回nil或lazy seq,递归调用来自lazy-seq
调用。