Clojure clojure.core.reducers/fold 和 Scala fold 有什么区别?



我发现Clojure具有clojure.core.reducers/fold功能。

此外,Scala 具有内置的fold功能,但无法理解它们的工作方式是否不同?

我假设你在谈论clojure.core.reducers/fold。

Scala 在序列上的默认fold实现非常简单:

collection.fold(identityElem)(binOp)

只需从identityElem开始,然后按顺序遍历集合,并将二进制运算binOp应用于已经累积的结果和当前序列值,例如

(1 to 3).fold(42000)(_ + _)

将导致42000 + 1 + 2 + 3=42006

带有完整签名的Clojurefold

(r/fold n combinef reducef coll)

从上面提到的包中分两个阶段并行工作。首先,它将输入分成大小n(大约(的较小组,然后使用reducef减少每个组,最后使用combinef组合每个组的结果。

主要区别在于combinef应该同时是零元二进制(Clojure具有多元函数(,并且(combinef)(没有参数(将被调用以为每个分区生成标识元素(因此,本文档是正确的,本文档是谎言(。

也就是说,为了从上面的例子中模拟 Scala 的折叠,必须编写这样的东西:

(require '[clojure.core.reducers :as r])
(r/fold 3 (fn ([] 42000) ([x y] y)) + [1 2 3])

总的来说,斯卡拉的fold

collection.fold(identityElement)(binOp)

可以通过reducers/fold模拟,如下所示:

(r/fold collectionSize (fn ([] identityElem) ([x y] y)) binOp collection)

(请注意抛弃第一个参数的([x y] y)装置,这是故意的(。

我想该接口并不打算与任何非幺半群的零二进制操作一起使用,这就是为什么 Scala 的fold使用 Clojure 的fold模拟如此尴尬的原因。如果你想要一些类似 Scalafold的东西,请在 Clojure 中使用reduce


编辑

哦,等等。文档实际上指出

combinef 必须是关联的,并且在调用时没有 参数,(组合(必须产生其标识元素

也就是说,我们实际上被迫使用幺半群作为combinef,所以上面的42000, ([x y] y)-example实际上是无效的,行为实际上是未定义的。我以某种方式将42006拿出来的事实在严格的技术意义上是一个黑客,它依赖于库函数的未定义行为来获得所需的结果42006

考虑到这些额外的信息,我不确定Scala的fold是否可以被Clojure的core.reducers/fold模拟。Clojure的fold似乎被限制为具有幺半群的约简,而Scala的折叠更接近一般List催化,但代价是平行性。

clojure.core.reducers命名空间是专为并行处理大型数据集而设计的专用实现。 您可以在此处找到完整的文档:

https://clojure.org/reference/reducers。

(r/fold reducef coll)
(r/fold combinef reducef coll)
(r/fold n combinef reducef coll)

R/fold 采用可约集合并将其划分为以下几组 大约 n(默认为 512(个元素。每个组使用 还原函数。reducef 函数将被调用,没有 参数以在每个分区中生成标识值。结果 然后使用组合减少这些减少(默认为 减少(功能。当调用时没有参数,(combinef( 必须 生成其标识元素 - 这将被多次调用。 操作可以并行执行。结果将保持秩序。


在你用尽你的机器之前,你应该坚持基本的reduce功能:

https://clojuredocs.org/clojure.core/reduce

这与 Scala 的fold函数基本相同:

(reduce + 0 [1 2 3 4 5]) => 15

其中函数签名为:

(reduce <op> <init-val> <collection-to-be-reduced> )

最新更新