我目前正在阅读 Clojure for the Brave and True 这本书,试图学习这门语言,但是我对懒惰的seqs有点挂了,我害怕{ {3}}。但是,根据这本书,这样的事情:
(defn wait-for-a-bit [arg]
(Thread/sleep 1000))
(defn get-map [seq]
(map wait-for-a-bit seq))
(time (first (get-map [1 2 3 4 5 6 7 8 9 0])))
只需要大约一秒的时间来处理,因为懒惰的seq的值不会被计算(实现?),直到它被访问为止。但是,当我运行上面的代码时,大约需要十秒钟,所以很明显,延迟计算没有发生。我查看了the book does a poor job explaining them上的文档,我认为我理解lazy-seq,但我想不是在map,reduce等的上下文中。
答案 0 :(得分:3)
默认情况下,延迟序列被分块。所以实际值是以大约30个左右的块计算的。这极大地减少了处理它们的上下文切换开销。
这里我将定义100个项目的序列并查看前两个项目:
hello.core> (def foo (map #(do (println "calculating " %)
%)
(range 100)))
#'hello.core/foo
hello.core> (first foo)
calculating 0
calculating 1
calculating 2
calculating 3
calculating 4
calculating 5
calculating 6
calculating 7
...
calculating 26
calculating 27
calculating 28
calculating 29
calculating 30
calculating 31
0
hello.core> (second foo)
1
这表明它在第一次实现任何项目时计算第一个块。
有些序列是分块而有些则不是。它取决于最初创建seq的函数,以决定它是否可以分块。 range
创建分块序列,而iterate
则不会。如果我们再次查看相同的示例,这次使用迭代而不是map生成seq,我们得到一个非分块的序列:
hello.core> (def foo (map #(do (println "calculating " %)
%)
(take 100 (iterate inc 0))))
#'hello.core/foo
hello.core> (first foo)
calculating 0
0
hello.core> (second foo)
calculating 1
1
并且每个项目在读取时计算。从理论上讲,这有效率的影响,虽然我一直在写Clojure,只要有人,并且从来没有见过这样的情况,这会对那些设计不佳的东西产生影响。