(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
完成整个序列之后,现在已经在内存中了。由于实施可能不得不遍历列表,为什么不算数呢?
第三是解决方法。这是惯用的吗?还有其他选择吗?
答案 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;如果传入地图,则获得地图等。