【问题标题】:Difference between fold and foldLeft or foldRight?fold 和 foldLeft 或 foldRight 之间的区别?
【发布时间】:2011-09-09 09:14:56
【问题描述】:

注意:我使用的是 Scala 2.8——这会是个问题吗?

为什么我不能像foldLeftfoldRight 一样使用fold 函数?

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)(_ + _)
    ^

【问题讨论】:

  • Scaladoc 的“T”是您复制的签名中的A
  • 您使用的是哪个版本的 Scala? IIRC fold 是 2.9 中的新功能。
  • scala-lang.org/api/2.8.0/scala/collection/immutable/List.htmlfold 出现在 2.9 中,有并行集合。
  • 哦。谢谢,我很难找到 2.8 文档 :-(
  • @akauppi 抱歉,我的问题是关于 Scala 库中缺少 fold 函数(这是由于旧版本的 Scala)。因此,我并不是说它是一个理论问题,许多人认为它是。所以答案让我满意。我觉得改变接受的答案也意味着改变问题本身。

标签: scala fold


【解决方案1】:

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

例如,如果我输入 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,两者都会为您提供具有相似性能的结果。

但是,如果您尝试在 Long List 上运行 FoldRight,它可能会引发 StackOverFlow 错误(取决于您的记忆力)

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

【讨论】:

    【解决方案2】:

    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
     * #######################
     */
    

    【讨论】:

      【解决方案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
      

      【讨论】:

        【解决方案4】:

        简答:

        foldRight 关联到右侧。 IE。元素将按从右到左的顺序累积:

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

        foldLeft 关联到左侧。 IE。一个累加器将被初始化,并且元素将按从左到右的顺序添加到累加器中:

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

        fold关联的,因为未定义元素添加在一起的顺序。 IE。 fold 的参数形成一个 monoid

        【讨论】:

        • foldLeftfoldRight 也是不可交换的(因此得名),而 fold 可能是也可能不是;我不禁认为这是一个奇怪的命名约定选择。
        • 抱歉,我不明白什么是幺半群,以及为什么我不能像 foldLeft 那样使用 fold。有关添加示例,请参阅我的问题。
        • 幺半群是一个关联二元函数以及该函数的标识元素。例如,(0)(_+_) 是一个幺半群。 (1)(_*_) 是另一个例子。我想你明白它是什么,只是不知道那个名字。
        • Rex:交换性在这里并不重要。
        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

          • 嗯..我试过了,还是不行。带有 fold 的行给了我一个错误(请参阅我的问题)。我正在使用 scala 2.8 - 也许这是个问题?
          【解决方案7】:

          foldfoldRightfoldLeft 不同,不对集合元素的处理顺序提供任何保证。您可能希望将fold 与并行集合一起使用,它的签名更受约束,其中缺乏保证的处理顺序有助于并行集合以并行方式实现折叠。更改签名的原因类似:通过附加约束,更容易进行平行折叠。

          【讨论】:

          • 你能提供一个在列表中添加所有元素的例子吗?我修改了我的问题!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-03-25
          • 2014-09-29
          • 2011-12-07
          • 2019-03-05
          • 2017-11-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多