clojure rest and next related



我在关注The Joy of Clojure,我对的这两个语句感到困惑

(def very-lazy (-> (iterate #(do (print .) (inc %)) 1) rest rest rest))
(def less-lazy (-> (iterate #(do (print .) (inc %)) 1) next next next))

因此,输出为

(println (first very-lazy)) ; .4
(println (first less-lazy)) ; 4

这本书继续解释

抓取用rest构建的惰性seq中的第一个元素会导致实现预期。但同样的情况不会发生在seq构建与next,因为它之前已经实现了。使用next导致lazy-seq比lazy少一个元素,这可能不是如果实现成本昂贵,则是期望的。

我迫切的问题是,为什么"非常懒惰"会多加一个点?我的想法是,无论是next还是rest,"print"都会在调用时打印其参数。

感谢

Print实际上在这两种情况下都做了完全相同的事情,只打印数字。额外的.是由列表中的代码打印的,它恰好与4的打印同时发生,因此它最终在屏幕上挨着它。

额外的点是动态创建的惰性序列的副作用。我想提出一个更详细的例子来澄清这一点:

从两个完全惰性的相同列表开始:

esc.core=> (def a (iterate #(do (print "making new element") (inc %)) 1)) 
#'esc.core/a
esc.core=> (def b (iterate #(do (print "making new element") (inc %)) 1))
#'esc.core/b

然后分别从CCD_ 5和CCD_ 6的第四个元素

esc.core=> (def a-partially-realized (-> a rest rest rest))
making new elementmaking new element#'esc.core/a-partially-realised
esc.core=> (def b-more-fully-realized (-> b next next next))
making new elementmaking new elementmaking new element#'esc.core/b-more-fully-realised
esc.core=> 

a-partially-realized的前三个元素已经被预先计算
而CCD_ 8的前4个元素已经被预先计算。

当我们读取a-partially-realized的第一个元素(原始列表中的第四个(时,它尚未被计算,因此我们将看到它正在被计算。

esc.core=> (print (first a-partially-realized))
making new element4nil

当我们对b-more-fully-realised做同样的操作时,它已经缓存了值,所以我们只需立即获得结果。

esc.core=> (print (first b-more-fully-realized))
4nil

您错过了一些重要的东西。点数:

user> (def very-lazy (-> (iterate #(do (print .) (inc %)) 1) rest rest rest))
..#'user/very-lazy
user> (def less-lazy (-> (iterate #(do (print .) (inc %)) 1) next next next))
...#'user/less-lazy

它总是有助于真实地运行代码,而不仅仅是看书。

正如Joost所指出的,当你定义非常懒惰和不那么懒惰时,魔法就会发生。

重点是,rest只需要返回一个序列。这个序列太懒了。因此,(->x rest rest(需要处理x的两个值,然后返回懒惰序列。因此,两个点。

另一方面,如果序列中没有更多元素,那么接下来必须返回nil,因此必须第三次评估x,以确定序列是否为空。三个点。

当你真正要求价值时,任何剩余的懒惰都需要被抛弃。所以,你可以多得到一个点来休息。下一个版本已经完成,所以不再有点了。

到目前为止,没有答案的是为什么nextrest更渴望,这是您所描述的差异的核心。对于长度小于2的任何列表,next返回nil。相反,rest在这些场景中返回空列表。这表明,当在1个元素的列表上调用next时,我们已经隐式地完成了对序列的处理(例如递归(,因此我们可以放心地假设是时候实现第一个元素了。这就解释了最后一个点实现得早与晚的原因。

next一被调用就执行#()函数,而rest则不执行,因为它还不必执行——它会等待尽可能长的时间——所以在本例中,直到调用另一个rest,或者需要返回值,打印时就是这样。

我也错过了第一次阅读的要点。rest只有两个点,因为还没有足够好的理由实现最后一个rest

最新更新