Clojure 中最简单的惰性函数
我很难理解懒惰。
有人可以帮助我理解为什么下面的函数不是惰性函数,
(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))))
I'm having a tough time understanding laziness.
Can someone help me understand why my function below is not lazy
(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 [])))))
whereas this example given on clojure.org/lazy is
(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))))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
filter
中的惰性来自于递归循环的if
分支中对filter
的调用。这就是代码遇到另一个lazy-seq并停止评估seq直到请求另一个元素的地方。您的
my-red
实现一次且仅一次调用了lazy-seq
,这意味着您的 seq 要么根本不评估,要么完全评估。The laziness in
filter
comes from the call tofilter
in theif
branch of the recursive loop. That's where the code hits anotherlazy-seq
and stops evaluating the seq until another element is requested.Your implementation of
my-red
hits a call tolazy-seq
one and only one time, meaning that your seq is either not evaluated at all or completely evaluated.mr 函数将在整个 coll 中重复出现。也许你的缩进误导了你。正确缩进,并删除了一些无用的参数,该函数看起来像这样:
基本上,您正在围绕 mr 函数进行包装(惰性序列),该函数在一个大的递归循环中完成所有工作。
The mr function will just recur through the whole of coll. Perhaps your indentation is misleading you. correctly indented, and with some useless parameters removed the function looks something like this:
Basically, you're wrapping (lazy-seq) around the mr function, which does all the work in one big recur loop.
lazy-seq 所做的只是获取其参数并延迟执行。为了产生真正的惰性序列,每个链接都必须包装在惰性序列调用中。惰性的“粒度”是在调用
lazy-seq
之间完成了多少工作。解决这个问题的一种方法是使用返回惰性序列的高级函数。此外,尾递归和惰性是相互排斥的。这不会导致堆栈溢出,因为在每一步中,递归调用都会被包装到惰性 seq 中并返回。如果调用者随后尝试计算惰性 seq,则会调用递归调用,但它是由序列函数的原始调用者调用的,而不是序列函数本身,这意味着堆栈不会增长。这有点类似于通过 Trampoline 实现优化尾递归的想法(参见 Clojure 的 Trampoline 函数)。
这是一个惰性版本:
请注意每次运行
mr
如何立即返回 nil 或惰性 seq,并且递归调用来自于lazy-seq
调用。All
lazy-seq
does is take its argument and delay execution of it. In order to produce a true lazy sequence, every link must be wrapped in a lazy-seq call. The "granularity" of the lazyness is how much work is done between calls tolazy-seq
. The one way to get around this is to use the higher level functions that return a lazy seq.Additionally, tail recursion and lazyness are mutually exclusive. This does not cause stack overflows because at each step, the recursive call is wrapped up into a lazy seq and returned. If the caller then tries to evaluate the lazy seq, the recursive call is called, but it is being called by the original caller of the sequence function, not the sequence function itself, which means that the stack doesn't grow. This is somewhat similar to the idea of implementing optimized tail recursion via trampolines (see Clojure's
trampoline
function).Here is a version that is lazy:
Note how each run of
mr
immediately returns either nil or a lazy seq, and the recursive call is from within thelazy-seq
call.