评论由:jdevuyst
嗯。我认为懒惰序列确实会导致许多分配。
好吧,我重新写了我的测试并运行了几次。现在我还在向量和数组上进行了测试。
第一个补丁需要稍作调整。当调用coll-fold时,第一个补丁只为类型{{object}}指定了后备(即调用r/reduce)。我不得不为类型{{array}}添加相同的后备。(这很奇怪!)
所以以下是结果。
对于向量
`
(let [coll (vec (repeat 100 (vec (range 100))))]
(time (dotimes [n 3000]
(->> coll
(r/mapcat identity)
(r/map inc)
(r/filter even?)
(r/fold +)))))
`
第一个补丁:205872 毫秒
第二个补丁:210756 毫秒
对于数组
`
(let [coll (into-array (repeat 100 (into-array (range 100))))]
(time (dotimes [n 3000]
(->> coll
(r/mapcat identity)
(r/map inc)
(r/filter even?)
(r/fold +)))))
`
第一个补丁:123567 毫秒
第二个补丁:119704 毫秒
我运行了我的测试几次,结果相当一致。第一个补丁对向量更快,第二个补丁对数组更快。
这很有道理。
在第一个补丁中,{{reducer}}将直接调用{{-reduce}}。在第二个补丁中,{{reducer}}首先调用{{r/reduce}},它会在集合是向量时调用{{-reduce}},如果是数组则调用{{array-reduce}}。因此,第二个补丁在向量的情况下包含一个额外的函数调用,但在数组的情况下避免了在原生类型上调用协议方法。
使用宏(或者复制粘贴)可以避免额外的函数调用。这值得尝试吗,或者保持代码清洁更重要?
--
我刚刚意识到第二个补丁在语义上略与Clojure不同,尽管这可能是Clojure的一个错误:。我的建议是使用宏(或者复制粘贴)来避免第二个补丁中额外的函数调用,也可能解决这个差异。