我了解了foldLeft
和之间的基本区别reduceLeft
foldLeft:
- 必须传递初始值
reduceLeft:
- 将集合的第一个元素作为初始值
- 如果collection为空,则引发异常
还有其他区别吗?
是否有两种特定功能具有相似功能的特定原因?
我了解了foldLeft
和之间的基本区别reduceLeft
foldLeft:
reduceLeft:
还有其他区别吗?
是否有两种特定功能具有相似功能的特定原因?
Answers:
在给出实际答案之前,这里要提到的几件事:
left
,而是关于缩小和折叠之间的区别回到您的问题:
这是foldLeft
(可能是foldRight
我要说的)的签名:
def foldLeft [B] (z: B)(f: (B, A) => B): B
这是reduceLeft
(再次的方向在这里无关紧要)的签名
def reduceLeft [B >: A] (f: (B, A) => B): B
两者看起来非常相似,因此引起了混乱。reduceLeft
是的一种特殊情况foldLeft
(顺便说一句,您有时可以通过使用它们之一来表达相同的内容)。
当你打电话reduceLeft
说在List[Int]
它的字面减少整数的整个列表为一个值,这将是类型Int
(或者的超类型Int
,因此[B >: A]
)。
当您foldLeft
在上说时,List[Int]
它会将整个列表折叠(想象一下要卷一张纸)成一个值,但是该值甚至不必与Int
(因此[B]
)相关。
这是一个例子:
def listWithSum(numbers: List[Int]) = numbers.foldLeft((List.empty[Int], 0)) {
(resultingTuple, currentInteger) =>
(currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}
此方法采用a List[Int]
并返回Tuple2[List[Int], Int]
or (List[Int], Int)
。它计算总和,并返回带有整数列表和其总和的元组。顺便说一下,列表是向后返回的,因为我们使用foldLeft
而不是foldRight
。
观看“ 一折”,将其全部统治,以进行更深入的说明。
B
是超型A
吗?似乎B
实际上应该是的子类型A
,而不是父类型。例如,假设Banana <: Fruit <: Food
,如果我们有一个Fruit
s 列表,则似乎其中可能包含一些Banana
s,但是如果它包含任何Food
s,则类型为Food
,对吗?因此,在这种情况下,如果B
是的超类型,A
并且存在同时包含B
s和A
s的列表,则该列表的类型应该B
不是A
。您能解释这个差异吗?
List[Banana]
可以将a简化为单个Banana
或单个Fruit
或单个Food
。因为Fruit :> Banana
和`Food:> Banana'。
Banana
可能包含Fruit
”,这没有任何意义。您的解释确实是有道理的- f
传递给该函数的reduce()
结果可能是a Fruit
或a Food
,这意味着B
签名中的应该是超类,而不是子类。
reduceLeft
只是一种方便的方法。相当于
list.tail.foldLeft(list.head)(_)
reducelft
虽然
fold
在空白列表上工作而在空白列表reduce
上不工作。
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
另一方面,如果它在列表中找不到至少一个值,则没有合法值。
它们都在Scala标准库中的基本原因可能是因为它们都在Haskell标准库中(称为foldl
和foldl1
)。如果reduceLeft
不是这样,通常会在不同项目中将其定义为一种便捷方法。
作为参考,reduceLeft
如果应用于具有以下错误的空容器,将出错。
java.lang.UnsupportedOperationException: empty.reduceLeft
修改代码以使用
myList foldLeft(List[String]()) {(a,b) => a+b}
是一种潜在的选择。另一种方法是使用reduceLeftOption
返回Option包装结果的变量。
myList reduceLeftOption {(a,b) => a+b} match {
case None => // handle no result as necessary
case Some(v) => println(v)
}
摘自Scala中的函数式编程原理(马丁·奥德斯基):
该函数
reduceLeft
是根据更通用的函数来定义的foldLeft
。
foldLeft
就像,reduceLeft
但是将一个accumulatorz
作为附加参数,当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
返回的type值U
不一定与相同List[T]
,但reduceLeft返回的值与列表相同。
要真正了解折叠/缩小功能,请检查以下内容:http : //wiki.tcl.tk/17983 很好的解释。一旦了解了折叠的概念,reduce将与上面的答案一起出现:list.tail.foldLeft(list.head)(_)