图是不是很懒?

map not quite lazy?

map 似乎并不像我希望的那样懒惰,在此示例中,map 按我的预期调用了一次函数:

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

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

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

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

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

感谢您的宝贵时间。

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

测试此行为时,尝试使用计数 > 32 的集合进行测试((vec (range 40)) returns 项目 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)。

如果集合是列表,则不使用分块序列,仅评估第一项((apply list (range 40)) returns 项 0-39 的列表):

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