为什么 Clojure 的核心化简器比惰性收集函数更快



在Reducers的许多资源中(如Rich Hickey的规范博客文章),声称Reducer比常规收集函数((map ... (filter ...))等)更快,因为开销更少。

避免的额外开销是什么?IIUC 即使是惰性收集函数,最终也只遍历一次原始序列。中间结果的计算方式是否有所不同?

指向 Clojure 实现中相关位置的指针也最有帮助

我认为一个关键的见解来自原始博客文章的以下段落:

(require '[clojure.core.reducers :as r])
(reduce + (r/filter even? (r/map inc [1 1 1 2])))
;=> 6

这应该看起来很熟悉 - 它是相同的命名函数,以相同的顺序应用,具有相同的参数,产生与 Clojure 基于 seq 的 fns 相同的结果。不同之处在于,减少急切,并且这些化简器 fns 不在 seq 游戏中,没有每步分配开销,因此速度更快。当你需要懒惰时,懒惰是很好的,但当你不需要它时,你不应该为此付出代价。

惰性序列的实现

伴随着(线性)分配成本:每次实现惰性序列中的另一个元素时,序列的其余部分都会存储在一个新的 thunk 中,并且这种"thunk"的表示是一个新的clojure.lang.LazySeq对象。

我相信这些LazySeq对象是报价中提到的分配开销。使用化简器,不会逐渐实现懒惰的 seq 元素,因此根本不会实例化LazySeq笨蛋。

最新更新