为什么`doall`不会强制对序列进行计数?

时间:2017-08-09 16:30:54

标签: clojure time-complexity

(counted? (map identity (range 100)))                  ;; false, expected 
(time (counted?  (doall  (map identity (range 100))))) ;; false, unexpected
(time (counted? (into '() (map identity (range 100))))) ;; true, expected - but slower

(Clojure "1.8.0"

第一个结果是预期的,因为map是懒惰的。

第二个对我来说意外,因为在doall完成整个序列之后,现在已经在内存中了。由于实施可能不得不遍历列表,为什么不算数呢?

第三是解决方法。这是惯用的吗?还有其他选择吗?

1 个答案:

答案 0 :(得分:3)

听起来你已经知道懒惰的序列不是counted?

但是,在您的示例中,虽然doall实现了整个序列,但它仍然将 结果作为LazySeq返回。看看这个REPL输出:

user=> (class (doall (map identity (range 100))))
clojure.lang.LazySeq

使用像into这样的东西似乎是找我的正确方法;因为你需要强制你的结果成为一个非懒惰的序列。你说into速度较慢,但​​对我来说似乎仍然可以接受。

尽管如此,您可以通过在结果上调用vec来改善时间效果,而不是into

user=> (time (counted? (into '() (map identity (range 100)))))
"Elapsed time: 0.287542 msecs"
true
user=> (time (counted? (vec (map identity (range 100)))))
"Elapsed time: 0.169342 msecs"
true

注意:我在机器上使用的是Clojure 1.9而不是1.8,因此您可能会看到不同的结果。

更新/更正:

评论者恭敬地指出:

1)time对于基准测试来说很糟糕,并且在这种情况下并没有提供任何有用的证据。

2)(vec x)代替(list x); (list x)是一个恒定时间操作,无论x的内容是什么。

3)doall返回其输入作为输出;如果您传入LazySeq,则会获得LazySeq;如果传入地图,则获得地图等。