评论由:jdevuyst
嗯。我想懒序列确实会导致很多分配。
好吧,我把测试重写并运行了几次。我现在还在向量上和数组上测试。
补丁 1 需要一些微调。当调用 coll-fold 时,补丁 1 只指定了 {{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 +)))))
`
补丁 1: 205872 毫秒
补丁 2: 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 +)))))
`
补丁 1: 123567 毫秒
补丁 2: 119704 毫秒
我运行了我的测试几次,结果相当一致。补丁 1 对向量更快,补丁 2 对数组更快。
这有道理。
在补丁 1 中,{{reducer}} 将直接调用 {{-reduce}}。在补丁 2 中,{{reducer}} 首先调用 {{r/reduce}},它会根据集合是向量还是数组来调用 {{-reduce}} 或 {{array-reduce}}。因此,在向量的情况下,补丁 2 包含一个额外的函数调用,但避免了在数组的情况下在一个原生类型上调用协议方法。
使用宏(或复制粘贴)可以避免额外的函数调用。这值得一试吗,或者保持代码整洁更重要?
--
我刚刚意识到,补丁 2 与 Clojure 的语义略有不同,尽管这可能是一个 Clojure 的错误: https://groups.google.com/forum/#!searchin/clojure-dev/kv-reduce/clojure-dev/bEqECvbExGo/iW4B2vEUh8sJ。我建议使用宏(或复制粘贴)来避免补丁 2 中的额外函数调用,这也可以解决这个差异。