懒惰的seqs不推迟计算

时间:2016-03-11 23:30:39

标签: clojure lazy-evaluation lazy-sequences

我目前正在阅读 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等的上下文中。

1 个答案:

答案 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,只要有人,并且从来没有见过这样的情况,这会对那些设计不佳的东西产生影响。