在许多关于Reducers的资源中(如 the canonical blog post by Rich Hickey ),声称Reducers比常规集合函数((map ... (filter ...))
等)更快,因为开销更少。
可以避免哪些额外开销? IIUC 即使是惰性收集函数最终也会遍历原始序列一次。中间结果的计算细节是否有差异?
指向 Clojure 实现中有助于理解差异的相关位置的指针也将是最有帮助的
最佳答案
我认为一个关键的见解来自 original blog post 的以下段落。 :
(require '[clojure.core.reducers :as r]) (reduce + (r/filter even? (r/map inc [1 1 1 2]))) ;=> 6
That should look familiar - it's the same named functions, applied in the same order, with the same arguments, producing the same result as the Clojure's seq-based fns. The difference is that, reduce being eager, and these reducers fns being out of the seq game, there's no per-step allocation overhead, so it's faster. Laziness is great when you need it, but when you don't you shouldn't have to pay for it.
延迟序列的实现会带来(线性)分配成本:每当延迟序列中的另一个元素被实现时,seq 的剩余部分就会存储在一个新的 thunk 中,并且这种“thunk”的表示是 a new clojure.lang.LazySeq
object .
我相信那些LazySeq
对象是引用中提到的分配开销。使用reducers,不会逐渐实现lazy seq元素,因此根本不会实例化LazySeq
thunk。
关于clojure - 为什么 Clojure 的 core.reducers 比惰性集合函数更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36011719/