fold和foldLeft或foldRight之间的区别?

时间:2011-06-06 14:58:55

标签: scala fold

注意:我在使用Scala 2.8 - 这可能是个问题吗?

为什么我不能像foldfoldLeft一样使用foldRight功能?

Set scaladoc中它说:

  

折叠的结果可能只是此并行集合的类型参数T的超类型。

但我在函数签名中看不到类型参数T

def fold [A1 >: A] (z: A1)(op: (A1, A1) ⇒ A1): A1

foldLeft-Rightfold之间有什么区别,我如何使用后者?

编辑:例如,如何编写折叠以添加列表中的所有元素?使用foldLeft,它将是:

val foo = List(1, 2, 3)
foo.foldLeft(0)(_ + _)

// now try fold:
foo.fold(0)(_ + _)
>:7: error: value fold is not a member of List[Int]
  foo.fold(0)(_ + _)
    ^

7 个答案:

答案 0 :(得分:66)

简短回答:

foldRight与右边相关联。即元素将按从右到左的顺序累积:

List(a,b,c).foldRight(z)(f) = f(a, f(b, f(c, z)))

foldLeft左侧同伴。即将初始化累加器,并按从左到右的顺序将元素添加到累加器:

List(a,b,c).foldLeft(z)(f) = f(f(f(z, a), b), c)

fold associative ,因为没有定义元素加在一起的顺序。即fold的参数构成 monoid

答案 1 :(得分:53)

foldfoldRight相反,

foldLeft并未提供有关处理集合元素的顺序的任何保证。您可能希望使用具有更多约束签名的fold和并行集合,其中缺少有保证的处理顺序有助于并行集合以并行方式实现折叠。更改签名的原因是类似的:通过附加约束,更容易进行并行折叠。

答案 2 :(得分:8)

你对Scala的旧版本是个问题是对的。如果您查看Scala 2.8.1的scaladoc page,您会看到没有定义折叠(这与您的错误消息一致)。显然,Scala 2.9中引入了fold

答案 3 :(得分:3)

对于您的特定示例,您将使用与foldLeft相同的方式对其进行编码。

val ns = List(1, 2, 3, 4)
val s0 = ns.foldLeft (0) (_+_) //10
val s1 = ns.fold (0) (_+_) //10
assert(s0 == s1)

答案 4 :(得分:3)

同意其他答案。考虑给出一个简单的说明性例子:

 object MyClass {
 def main(args: Array[String]) {
val numbers = List(5, 4, 8, 6, 2)
 val a =  numbers.fold(0) { (z, i) =>
 {
     println("fold val1 " + z +" val2 " + i)
  z + i

 }
}
println(a)
 val b =  numbers.foldLeft(0) { (z, i) =>
 println("foldleft val1 " + z +" val2 " + i)
  z + i

}
println(b)
   val c =  numbers.foldRight(0) { (z, i) =>
   println("fold right val1 " + z +" val2 " + i)
  z + i

}
println(c)
 }
}

结果是不言自明的:

fold val1 0 val2 5
fold val1 5 val2 4
fold val1 9 val2 8
fold val1 17 val2 6
fold val1 23 val2 2
25
foldleft val1 0 val2 5
foldleft val1 5 val2 4
foldleft val1 9 val2 8
foldleft val1 17 val2 6
foldleft val1 23 val2 2
25
fold right val1 2 val2 0
fold right val1 6 val2 2
fold right val1 8 val2 8
fold right val1 4 val2 16
fold right val1 5 val2 20
25

答案 5 :(得分:0)

fold()进行并行处理,因此不能保证处理顺序。 就像foldLeft和foldRight一样,从左到右(对于foldLeft)或从右到左(对于foldRight)依次处理项目

列表总和的示例-

val numList = List(1, 2, 3, 4, 5)

val r1 = numList.par.fold(0)((acc, value) => {
  println("adding accumulator=" + acc + ", value=" + value + " => " + (acc + value))
  acc + value
})
println("fold(): " + r1)
println("#######################")
/*
 * You can see from the output that,
 * fold process the elements of parallel collection in parallel
 * So it is parallel not linear operation.
 * 
 * adding accumulator=0, value=4 => 4
 * adding accumulator=0, value=3 => 3
 * adding accumulator=0, value=1 => 1
 * adding accumulator=0, value=5 => 5
 * adding accumulator=4, value=5 => 9
 * adding accumulator=0, value=2 => 2
 * adding accumulator=3, value=9 => 12
 * adding accumulator=1, value=2 => 3
 * adding accumulator=3, value=12 => 15
 * fold(): 15
 */

val r2 = numList.par.foldLeft(0)((acc, value) => {
  println("adding accumulator=" + acc + ", value=" + value + " => " + (acc + value))
  acc + value
})
println("foldLeft(): " + r2)
println("#######################")
/*
 * You can see that foldLeft
 * picks elements from left to right.
 * It means foldLeft does sequence operation
 * 
 * adding accumulator=0, value=1 => 1
 * adding accumulator=1, value=2 => 3
 * adding accumulator=3, value=3 => 6
 * adding accumulator=6, value=4 => 10
 * adding accumulator=10, value=5 => 15
 * foldLeft(): 15
 * #######################
 */

// --> Note in foldRight second arguments is accumulated one.
val r3 = numList.par.foldRight(0)((value, acc) => {
 println("adding value=" + value + ", acc=" + acc + " => " + (value + acc))
  acc + value
})
println("foldRight(): " + r3)
println("#######################")

/*
 * You can see that foldRight
 * picks elements from right to left.
 * It means foldRight does sequence operation.
 * 
 * adding value=5, acc=0 => 5
 * adding value=4, acc=5 => 9
 * adding value=3, acc=9 => 12
 * adding value=2, acc=12 => 14
 * adding value=1, acc=14 => 15
 * foldRight(): 15
 * #######################
 */

答案 6 :(得分:0)

有两种解决问题的方法,即迭代和递归。让我们通过一个简单的例子来理解。让我们编写一个函数来求和直到给定的数字。

例如,如果我将输入设为5,则应该得到15作为输出,如下所述。

输入:5

输出 :( 1 + 2 + 3 + 4 + 5)= 15

迭代解决方案。

重复1到5并求和每个元素。

  def sumNumber(num: Int): Long = {
    var sum=0
    for(i <- 1 to num){
      sum+=i
    }
    sum
  }

递归解决方案

将较大的问题分解为较小的问题并加以解决。

  def sumNumberRec(num:Int, sum:Int=0): Long = {
    if(num == 0){
      sum
    }else{
      val newNum = num - 1
      val newSum = sum + num
      sumNumberRec(newNum, newSum)
    }
  }

FoldLeft :是一种迭代解决方案

FoldRight :是一种递归解决方案 我不确定他们是否有备忘录来提高复杂性。

因此,如果您在小型列表上运行foldRight和FoldLeft,则两者都将为您提供性能相似的结果。

但是,如果您尝试在长列表上运行FoldRight,则可能会引发 StackOverFlow 错误(取决于您的内存)

检查以下屏幕截图,其中foldLeft正常运行,但是同一列表上的foldRight给出了OutofMemmory错误。

enter image description here