【发布时间】:2014-08-05 22:27:01
【问题描述】:
阅读这篇关于 Scala http://josephmoniz.github.io/blog/2013/04/04/scala-reduce-vs-fold/ 中的 reduce 与 fold 的文章指出“您正在获取 N 的某个值并对其执行聚合操作,因此最终结果通常是
但是由于对 N 个值求和会产生一个值 >= N,因此该陈述是否错误?
更新:我认为
【问题讨论】:
标签: scala
阅读这篇关于 Scala http://josephmoniz.github.io/blog/2013/04/04/scala-reduce-vs-fold/ 中的 reduce 与 fold 的文章指出“您正在获取 N 的某个值并对其执行聚合操作,因此最终结果通常是
但是由于对 N 个值求和会产生一个值 >= N,因此该陈述是否错误?
更新:我认为
【问题讨论】:
标签: scala
我认为这是一个有缺陷的表征。最好这样考虑折叠:
In:
initial value
way to combine stuff with initial value
collection
Out:
combined stuff
而reduce是这样的:
In:
way to combine stuff
collection
Out:
combined stuff
也就是说,区别在于您是否有一个初始值(它甚至可能与您在集合中获得的类型不同!),就像 fold 一样,或者您是否只是折叠已经存在的值有,就像 reduce 一样。
如果你有一个自然零,即可以组合而不改变它所结合的东西,那么你可以将 reduce 实现为从零开始的折叠。例如,对于乘法,零是1(因为1*x == x),所以
List(1,2,3).fold(1){_ * _}
List(1,2,3).reduce{_ * _}
给出相同的答案。 (然而,只有第一个在空列表上给出答案!)
要查看 fold 更通用的示例,请考虑这个示例——这里使用 foldLeft,因此我们知道在操作的左侧传递初始值——
List(1,2,3).foldLeft(List(0))((ns,n) => ns ++ List.fill(n+1)(n))
给出List(0, 1, 1, 2, 2, 2, 3, 3, 3, 3)。
【讨论】:
Fold 需要提供“起始元素”,reduce 会自动将序列的第一个元素作为起始元素,所以它们在某种程度上是等价的:
val L = List(1,2,3,4)
val H = L.head
val T = L.tail
L.reduce(_+_) ~== T.fold(H)(_+_)
reduce 更紧凑,但是使用 fold 你可以提供不同的起始元素并改变操作的结果,所以:
2014 + L.reduce(_+_) ~== L.fold(2014)(_+_) // NB: here we use L, not T for fold
当您从简单的算术跳到一些更复杂的二元运算(如 Set + Int)时,事情会更令人兴奋,也更有利于折叠:
List(1,2,3,3,2,2,1).foldLeft(Set[Int]())(_ + _) // will produce Set(1, 2, 3)
...您可以折叠 JDBC 更新调用:)。
【讨论】:
L.reduce(_ + _) == L.fold(0)(_ + _)。所以这个例子,2014 + L.reduce(_ + _) ~== L.fold(2014)(_ + _) 并没有真正误导
reduce 使用称为“monoid”的概念来获取“零值”作为折叠中累加器的初始化器*
【讨论】:
在这里查看答案:
difference between foldLeft and reduceLeft in Scala
reduce 和 fold 是 reduceLeft 和 foldLeft 的快捷键
【讨论】:
reduceLeft 和 foldLeft 从左到右(从头到尾)应用运算符。 reduceRight 和 foldRight 从右到左应用它。至于reduce 和fold,顺序是未指定,这允许对其进行并行实现(参见ParIterableLike)。因此,在调用reduce 时,您最好确保您的操作符是关联的,否则结果将不确定。