地图不是很懒?

时间:2016-10-10 11:33:26

标签: clojure lazy-evaluation

地图似乎并不像我想的那样懒,在这个示例中,地图按照我的预期调用函数一次:

(first (map #(do (println "x: " %) %) '(0 1)))

但在这两个例子中,它调用了两次函数:

(first (map #(do (println "x: " %) %) '[0 1]))
(first (map #(do (println "x: " %) %) (doall (range 2))))

选择是否懒惰的基本原则是什么?

有没有办法保证完全懒惰?

感谢您的时间。

1 个答案:

答案 0 :(得分:4)

Map(以及处理集合的类似HOF)处理集合上的序列抽象:它从传递的集合(seq coll)创建一个序列,然后处理返回的序列。 PersistentList'(0 1)PersistentList的实例)通过ISeq扩展名实现ASeq,因此seq函数会返回列表本身。在PersistentVector[1 2])的情况下,seq函数返回一个分块序列(性能原因,它在一个步骤中评估数据的块(32个),但只返回请求的元素;当前块耗尽时计算下一个块。)

测试此行为时,尝试使用count>进行集合测试32((vec (range 40))返回项目0-39的向量):

=> (first (map #(do (println "x: " %) %) (vec (range 40))))
x:  0
x:  1
x:  2
x:  3
...
x:  30
x:  31
0

如您所见,在访问第一个元素时会计算整个块(元素0-31),在从下一个块请求第一个元素时将评估其余元素(32-39)。

如果集合是列表,则不使用chunked seq,只评估第一个项目((apply list (range 40))返回项目列表0-39):

=> (first (map #(do (println "x: " %) %) (apply list (range 40))))
x:  0
0