惰性序列不延迟计算
Lazy seqs not deferring computation
我目前正在阅读 Clojure for the Brave and True 这本书,试图学习这门语言,但我对 lazy seqs 有点着迷,并且恐怕the book does a poor job explaining them。但是,根据这本书,是这样的:
(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])))
应该只需要大约一秒钟的时间来处理,因为惰性序列的值在被访问之前不会被计算(实现?)。但是,当我 运行 上面的代码时,大约需要十秒钟,很明显,延迟计算没有发生。我查看了 clojuredocs.org 上的文档,我想我了解 lazy-seq,但我想只是不在 map、reduce 等上下文中。
惰性序列默认分块。所以实际值是以 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
不创建。如果我们再次看同一个例子,这次使用 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 的时间与任何人一样长,而且从未见过这会对设计不佳的东西产生影响的情况。
我目前正在阅读 Clojure for the Brave and True 这本书,试图学习这门语言,但我对 lazy seqs 有点着迷,并且恐怕the book does a poor job explaining them。但是,根据这本书,是这样的:
(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])))
应该只需要大约一秒钟的时间来处理,因为惰性序列的值在被访问之前不会被计算(实现?)。但是,当我 运行 上面的代码时,大约需要十秒钟,很明显,延迟计算没有发生。我查看了 clojuredocs.org 上的文档,我想我了解 lazy-seq,但我想只是不在 map、reduce 等上下文中。
惰性序列默认分块。所以实际值是以 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
不创建。如果我们再次看同一个例子,这次使用 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 的时间与任何人一样长,而且从未见过这会对设计不佳的东西产生影响的情况。