【问题标题】:difference between foldLeft and reduceLeft in ScalaScala中foldLeft和reduceLeft之间的区别
【发布时间】:2011-12-07 12:43:33
【问题描述】:

我已经学会了foldLeftreduceLeft之间的基本区别

左折叠:

  • 必须传递初始值

reduceLeft:

  • 将集合的第一个元素作为初始值
  • 如果集合为空则抛出异常

还有其他区别吗?

有两个具有相似功能的方法的具体原因吗?

【问题讨论】:

标签: scala functional-programming fold higher-order-functions


【解决方案1】:

reduceLeft 只是一种方便的方法。相当于

list.tail.foldLeft(list.head)(_)

【讨论】:

  • 好答案。这也突出了为什么fold 可以在空列表上工作而reduce 不能。
【解决方案2】:

在给出实际答案之前,这里有几件事要提:

  • 您的问题与left 无关,而是关于减少和折叠之间的区别
  • 区别根本不在于实现,只看签名。
  • 这个问题与 Scala 没有任何关系,而是与函数式编程的两个概念有关。

回到你的问题:

这是foldLeft 的签名(也可以是foldRight,因为我要说明这一点):

def foldLeft [B] (z: B)(f: (B, A) => B): B

这里是reduceLeft 的签名(同样,这里的方向无关紧要)

def reduceLeft [B >: A] (f: (B, A) => B): B

这两个看起来非常相似,因此引起了混乱。 reduceLeftfoldLeft 的一个特例(顺便说一下,这意味着您有时可以使用它们中的任何一个来表达相同的东西)。

当你调用reduceLeft 时说List[Int],它实际上会将整个整数列表缩减为单个值,该值将是Int 类型(或Int 的超类型,因此@ 987654334@).

当您在List[Int] 上调用foldLeft 时,它会将整个列表(想象一下滚动一张纸)折叠成一个值,但这个值甚至不必与Int 相关(因此[B])。

这是一个例子:

def listWithSum(numbers: List[Int]) = numbers.foldLeft((List.empty[Int], 0)) {
   (resultingTuple, currentInteger) =>
      (currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}

此方法接受List[Int] 并返回Tuple2[List[Int], Int](List[Int], Int)。它计算总和并返回一个带有整数列表的元组,它是总和。顺便说一下,列表是向后返回的,因为我们使用了foldLeft 而不是foldRight

观看One Fold to rule them all 以获得更深入的解释。

【讨论】:

  • 你能解释一下为什么BA 的超类型吗?看起来B 实际上应该是A 的子类型,而不是超类型。例如,假设Banana <: Fruit <: Food,如果我们有一个Fruits 的列表,似乎它可能包含一些Bananas,但如果它包含任何Foods 那么类型将是Food,正确?所以在这种情况下,如果BA 的超类型,并且有一个包含Bs 和As 的列表,则该列表应该是B 类型,而不是A。你能解释一下这种差异吗?
  • 我不确定我是否正确理解了您的问题。我 5 年前的回答是关于 reduce 函数的意思是 List[Banana] 可以简化为单个 Banana 或单个 Fruit 或单个 Food。因为Fruit :> Banana 和`Food :> Banana'。
  • 是的……这确实有道理,谢谢。我最初将其解释为“Banana 类型的列表可能包含Fruit”,这是没有意义的。您的解释确实有道理——传递给reduce()f 函数可能导致FruitFood,这意味着签名中的B 应该是超类,而不是子类。跨度>
【解决方案3】:

foldLeft 更通用,您可以使用它来产生与您最初放入的完全不同的东西。而reduceLeft 只能产生与集合类型相同类型或超类型的最终结果。例如:

List(1,3,5).foldLeft(0) { _ + _ }
List(1,3,5).foldLeft(List[String]()) { (a, b) => b.toString :: a }

foldLeft 将使用最后一个折叠结果(第一次使用初始值)和下一个值应用闭包。

reduceLeft 另一方面,将首先组合列表中的两个值并将它们应用于闭包。接下来,它将其余值与累积结果相结合。见:

List(1,3,5).reduceLeft { (a, b) => println("a " + a + ", b " + b); a + b }

如果列表为空foldLeft 可以将初始值作为合法结果。另一方面,reduceLeft 如果在列表中找不到至少一个值,则它不具有合法值。

【讨论】:

    【解决方案4】:

    它们都在 Scala 标准库中的基本原因可能是因为它们都在 Haskell 标准库中(称为 foldlfoldl1)。如果reduceLeft 不是,它通常会被定义为不同项目中的便捷方法。

    【讨论】:

      【解决方案5】:

      作为参考,reduceLeft 应用到空容器时会出错,并出现以下错误。

      java.lang.UnsupportedOperationException: empty.reduceLeft
      

      修改代码以使用

      myList foldLeft(List[String]()) {(a,b) => a+b}
      

      是一种潜在的选择。另一种是使用reduceLeftOption 变体,它返回一个选项包装的结果。

      myList reduceLeftOption {(a,b) => a+b} match {
        case None    => // handle no result as necessary
        case Some(v) => println(v)
      }
      

      【讨论】:

        【解决方案6】:

        要真正了解您在使用 fold/reduce 做什么, 检查这个:http://wiki.tcl.tk/17983 很好的解释。一旦你有了折叠的概念, reduce 将与上面的答案一起出现: list.tail.foldLeft(list.head)(_)

        【讨论】:

          【解决方案7】:

          来自Functional Programming Principles in Scala(马丁·奥德斯基):

          函数reduceLeft 是根据更通用的函数foldLeft 定义的。

          foldLeftreduceLeft 类似,但将累加器 z 作为附加参数,在对空列表调用foldLeft 时返回:

          (List (x1, ..., xn) foldLeft z)(op) = (...(z op x1) op ...) op x

          [与 reduceLeft 不同,后者在空列表上调用时会引发异常。]

          本课程(参见第 5.5 节)提供了这些函数的抽象定义,说明了它们的区别,尽管它们在模式匹配和递归的使用上非常相似。

          abstract class List[T] { ...
            def reduceLeft(op: (T,T)=>T) : T = this match{
              case Nil     => throw new Error("Nil.reduceLeft")
              case x :: xs => (xs foldLeft x)(op)
            }
            def foldLeft[U](z: U)(op: (U,T)=>U): U = this match{
              case Nil     => z
              case x :: xs => (xs foldLeft op(z, x))(op)
            }
          }
          

          注意foldLeft返回一个U类型的值,它不一定与List[T]类型相同,但reduceLeft返回一个与列表相同类型的值。

          【讨论】:

            【解决方案8】:

            Scala 2.13.3,演示:

            val names = List("Foo", "Bar")
            println("ReduceLeft: "+ names.reduceLeft(_+_))
            println("ReduceRight: "+ names.reduceRight(_+_))
            println("Fold: "+ names.fold("Other")(_+_))
            println("FoldLeft: "+ names.foldLeft("Other")(_+_))
            println("FoldRight: "+ names.foldRight("Other")(_+_))
            

            输出:

            ReduceLeft: FooBar
            ReduceRight: FooBar
            Fold: OtherFooBar
            FoldLeft: OtherFooBar
            FoldRight: FooBarOther
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-09-29
              • 1970-01-01
              • 2011-09-09
              • 2013-06-16
              • 2021-02-19
              • 2019-04-25
              • 1970-01-01
              • 2010-12-17
              相关资源
              最近更新 更多