(let [sleep-and-wait
(map (fn [time]
(future
(Thread/sleep time)
(println (str "slept " time " sec" ))))
[4000 5000])]
(doall (map deref sleep-and-wait))
(println "done"))
由于map
生成了一个懒惰的序列,我希望future
没有启动,直到我们在其上调用deref
。预计deref
将阻止,直到将来返回结果。我们按顺序排列map
个元素,所以我希望这个代码运行9秒,但它运行在5。
有人可以解释原因吗?
答案 0 :(得分:3)
Clojure lazy-seqs并没有承诺最大限度地懒惰。
+user=> (take 1 (for [i (range 1000)] (doto i (println " - printed"))))
(0 - printed
1 - printed
2 - printed
3 - printed
4 - printed
5 - printed
6 - printed
7 - printed
8 - printed
9 - printed
10 - printed
11 - printed
12 - printed
13 - printed
14 - printed
15 - printed
16 - printed
17 - printed
18 - printed
19 - printed
20 - printed
21 - printed
22 - printed
23 - printed
24 - printed
25 - printed
26 - printed
27 - printed
28 - printed
29 - printed
30 - printed
31 - printed
0)
当在向量上调用seq时(大多数惰性操作在其集合参数上隐式调用seq),它会生成一个分块数据类型,它一次实现批量结果,而不是一个接一个地实现。如果您需要控制数据的消耗,您可以做一些强制取消分块的事情。
+user=>
(defn unchunk [s]
(when (seq s)
(lazy-seq
(cons (first s)
(unchunk (rest s))))))
#'user/unchunk
+user=> (take 1 (for [i (unchunk (range 1000))] (doto i (println " - printed"))))
(0 - printed
0)
当然,在这种情况下更简单的选择是使用非分块的类型
+user=> (take 1 (for [i (take 1000 (iterate inc 0))] (doto i (println " - printed"))))
(0 - printed
0)
答案 1 :(得分:0)
这可能与map
函数的性质有关:它不是逐个采用映射集合的元素,而是通过块进行优化。这是一个小例子:
user> (defn trace [x]
(println :realizing x)
x)
#'user/trace
user> (def m (map trace (range 1000)))
#'user/m
user> (first m)
:realizing 0
:realizing 1
:realizing 2
...
:realizing 30
:realizing 31
0
所以在你调用map的情况下,它不会在一个单独的线程中启动一个未来,而是启动它们,因此你只需要阻塞直到运行时间最长的线程完成(并且它持续5秒)