在许多关于Reducers的资源中(例如Rich Hickey的经典博客文章),声称Reducers比常规集合函数(
(map ... (filter ...))
等)更快,因为减少了开销。
避免的额外开销是什么?如果我理解正确,即使是惰性集合函数最终也会遍历原始序列一次。差异在于如何计算中间结果吗?
指向Clojure实现中有助于理解差异的相关位置的指针也将非常有帮助。
(map ... (filter ...))
等)更快,因为减少了开销。
避免的额外开销是什么?如果我理解正确,即使是惰性集合函数最终也会遍历原始序列一次。差异在于如何计算中间结果吗?
指向Clojure实现中有助于理解差异的相关位置的指针也将非常有帮助。
(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.
实现惰性序列的过程中会带来(线性)分配成本:每次实现惰性序列的另一个元素时,该序列的剩余部分都存储在一个新的thunk中,并且这种“thunk”的表示形式是新的clojure.lang.LazySeq
对象。
我认为这些LazySeq
对象是引用中提到的分配开销。使用reducers时,没有惰性序列元素的逐步实现,因此根本不需要实例化LazySeq
thunk。