map not quite lazy?

守給你的承諾、 提交于 2019-12-22 12:26:34

问题


map doesn't seem quite as lazy as I would like, in this example map calls the function one time as I would expect:

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

but in these two examples it calls the function two times:

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

What is the underlying principle for making the choice to be lazy or not?

Is there a way to guarantee total laziness?

Thanks for your time.


回答1:


Map (and similar HOFs that work on collections) works on the sequence abstraction over collections: it creates a sequence from the passed collection (seq coll) and works on the returned sequence afterwards. PersistentList ('(0 1) is instance of PersistentList) implements ISeq through the ASeq extension, so seq function returns the list itself. In case of PersistentVector ([1 2]), the seq function returns a chunked sequence (performance reasons, it evaluates chunk (32 elts) of data in one step, but returns only the requested element; the next chunk is calculated when the current chunk is exhausted).

When testing this behaviour, try to test with collections with count > 32 ((vec (range 40)) returns a vector of items 0-39):

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

As you can see, the whole chunk (elements 0-31) is evaluated when accessing the first element, the remaining elements (32-39) will be evaluated when requesting the first element from the next chunk.

In case of collections that are list, chunked seq is not used and only the first item is evaluated ((apply list (range 40)) returns a list of items 0-39):

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


来源:https://stackoverflow.com/questions/39957310/map-not-quite-lazy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!