我发现Clojure具有clojure.core.reducers/fold
功能。
Scala还具有内置的fold
函数,但不知道它们的工作方式是否不同?
答案 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
变形,而以并行性为代价。