Scala中foldLeft和reduceLeft之间的区别

时间:2011-10-14 07:08:45

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

我已经了解了foldLeftreduceLeft

之间的基本区别

foldLeft:

  • 必须传递初始值

reduceLeft:

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

还有其他区别吗?

有两种方法具有相似功能的任何特定原因吗?

8 个答案:

答案 0 :(得分:280)

在给出实际答案之前,这里很少提及:

  • 您的问题与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,因此[B >: A])。

当你在foldLeft上打电话List[Int]时,它会将整个列表(想象一张纸)折叠成一个值,但这个值甚至不必与Int(因此[B])。

以下是一个例子:

def listWithSum(numbers: List[Int]) = numbers.foldLeft((List[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以获得更深入的解释。

答案 1 :(得分:182)

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

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

答案 2 :(得分:43)

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如果在列表中找不到至少一个值,则没有合法值。

答案 3 :(得分:5)

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

答案 4 :(得分:5)

作为参考,reduceLeft如果应用于具有以下错误的空容器,则会出错。

java.lang.UnsupportedOperationException: empty.reduceLeft

重新编写代码以使用

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

是一个潜在的选择。另一种方法是使用返回Option包装结果的reduceLeftOption变体。

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

答案 5 :(得分:2)

来自Functional Programming Principles in Scala(Martin Odersky):

  

函数reduceLeft是根据更一般的函数foldLeft定义的。

     

foldLeftreduceLeft类似,但需要累加器 z作为附加参数,在foldLeft上调用时会返回该参数空列表:

     

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

[与reduceLeft相反,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) } } 在空列表中调用时抛出异常。]

该课程(见讲座5.5)提供了这些函数的抽象定义,这些定义说明了它们之间的差异,尽管它们在模式匹配和递归的使用方面非常相似。

foldLeft

请注意,U返回类型为List[T]的值,其类型不一定与if (session_status() == PHP_SESSION_NONE) { session_start(); } 相同,但reduceLeft返回与列表类型相同的值。 / p>

答案 6 :(得分:0)

要真正理解你在做什么用fold / reduce, 检查一下:http://wiki.tcl.tk/17983 非常好的解释。一旦你得到折叠的概念, 减少将与上面的答案一起: list.tail.foldLeft(list.head)(_)

答案 7 :(得分:0)

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