我发现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> )