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

时间:2018-08-30 22:50:03

标签: scala clojure

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

Scala还具有内置的fold函数,但不知道它们的工作方式是否不同?

2 个答案:

答案 0 :(得分:2)

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函数。 reducef函数将不带任何调用   在每个分区中生成标识值的参数。结果   然后使用combinatef减少这些减少中的一部分(默认为   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> )

答案 1 :(得分:2)

我假设您是在谈论clojure.core.reducers/fold

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

collection.fold(identityElem)(binOp)

简单地从identityElem开始,然后依次遍历该集合,然后对已经累加的结果和当前序列值(例如

)应用二进制运算binOp
(1 to 3).fold(42000)(_ + _)

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

带有完整签名的Clojure的fold

(r/fold n combinef reducef coll)
上述软件包中的

分两个阶段并行工作。首先,它将输入分成大约n个较小的组,然后使用reducef缩小每个组,最后使用combinef合并每个组的结果。

主要区别在于,combinef期望同时是零级二进制(Clojure具有多元函数)和(combinef)(无参数)将被调用以为每个分区生成标识元素(因此this documentation是正确的,而this documentation则是谎言)。

也就是说,为了从上述示例中模拟Scala的折痕,必须编写如下代码:

(require '[clojure.core.reducers :as r])

(r/fold 3 (fn ([] 42000) ([x y] y)) + [1 2 3])

通常,Scala的fold

collection.fold(identityElement)(binOp)
可由reducers/fold模仿

如下:

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

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

我猜该接口不适合用于任何不是monoid的零二进制操作,这就是为什么Scala的fold如此笨拙地使用Clojure的fold模拟的原因。如果您想要行为类似于Scala的fold,请在Clojure中使用reduce


编辑

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

  

combinef必须是关联的,并且在不使用   参数,(combinef)必须产生其标识元素

,也就是说,实际上我们被强制使用一个等号作为combinef,因此上述42000, ([x y] y)示例实际上是无效的,行为实际上是未定义的。从某种严格的技术意义上来说,我以某种方式退出42006是一个 hack ,它依赖于库函数的未定义行为来获得所需的结果42006。 / p>

考虑到这些额外的信息,我不确定Scala的fold是否可以完全由Clojure的core.reducers/fold模拟。 Clojure的fold似乎仅限于使用类半体词的减少,而Scala的折叠更接近于一般的List变形,而以并行性为代价。

相关问题