7

我正在关注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 构建的惰性序列中的第一个元素会导致预期的实现。但是用 next 构建的 seq 不会发生同样的情况,因为它之前已经实现了。使用 next 会导致惰性 seq 的元素变得不那么惰性,如果实现成本很高,这可能是不希望的。

我迫切的问题是为什么“非常懒惰”有一个额外的点?我的想法是 'print' 会在调用时打印它的参数,无论 ifnextrest.

谢谢

4

5 回答 5

7

Print 实际上在两种情况下都做了完全相同的事情,它只打印了数字。extra.是由列表内的代码打印的,它恰好与打印的同时发生,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

然后再制作两个相同的列表,分别从a和的第四个元素开始b

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已预先计算
,而 的前四个元素b-more-fully-realized已预先计算。

当我们读取它的第一个元素(原始列表中的第四个)时,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
于 2011-11-01T18:47:52.683 回答
5

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

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

它总是有助于真正运行代码,而不仅仅是阅读一本书。

于 2011-11-01T17:48:55.313 回答
4

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

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

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

当您实际要求该值时,需要丢弃任何剩余的惰性。所以,你得到一个多点休息。下一个版本已经完成,所以没有更多的点了。

于 2011-11-01T21:19:07.017 回答
1

到目前为止尚未回答的是为什么比您描述的差异的核心next渴望。返回长度小于 2 的任何列表。相反,在这些场景中返回空列表。这表明当调用一个包含 1 个元素的列表时,我们隐式地完成了对序列的处理(例如递归),因此我们可以放心地假设是时候实现第一个元素了。这解释了最终点的早期实现和晚期实现。restnextnilrestnext

于 2011-11-01T19:34:59.527 回答
0

next在调用函数时立即执行该#()函数,而rest不是因为它还没有必要 - 它等待尽可能长的时间 - 所以在这个例子中没有实现/执行发生,直到另一个rest被调用,或者需要返回值,打印时就是这种情况。

我也错过了初读时的点点滴滴。只有两个点是rest因为还没有足够好的理由rest来实现最后一个。

于 2015-07-12T23:58:54.717 回答