函数reduceLeft是根据更通用的函数foldLeft定义的 . foldLeft类似于reduceLeft,但是接受一个累加器z作为附加参数,当在空列表上调用foldLeft时返回该参数:( List(x1,...,xn)foldLeft z)(op)=(...( z op x1)op ...)op x
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返回与列表类型相同的值 .
7 回答
在给出实际答案之前,这里很少提及:
你的问题与
left
没有任何关系,而是减少和折叠之间的区别差别不是实现,只需看一下签名即可 .
这个问题并不是函数式编程的两个概念 .
Back to your question:
这是
foldLeft
的签名(对于我要提出的观点,也可能是foldRight
):这里是
reduceLeft
的签名(这里方向无关紧要)这两个看起来非常相似,从而引起了混乱 .
reduceLeft
是foldLeft
的一个特例(顺便说一句,你可以通过使用其中任何一个来表达相同的东西) .当你在
List[Int]
上调用reduceLeft
时,它会将整个整数列表减少为单个值,这将是Int
类型(或者是Int
的超类型,因此[B >: A]
) .当你在
List[Int]
上调用foldLeft
时,它会将整个列表(想象滚动一张纸)折叠成单个值,但这个值不必与Int
(因此[B]
)相关 .这是一个例子:
此方法采用
List[Int]
并返回Tuple2[List[Int], Int]
或(List[Int] -> Int)
. 它计算总和并返回一个带有整数列表的元组,它的总和 . 顺便说一下,列表向后返回,因为我们使用了foldLeft
而不是foldRight
.请观看One Fold to rule them all以获得更深入的解释 .
reduceLeft
只是一种方便的方法 . 它相当于foldLeft
更通用,您可以使用它来生成与您最初放入的内容完全不同的内容 . 而reduceLeft
只能生成相同类型或超类型集合类型的最终结果 . 例如:foldLeft
将使用最后折叠结果(第一次使用初始值)和下一个值应用闭包 .另一方面,
reduceLeft
将首先组合列表中的两个值并将其应用于闭包 . 接下来,它将其余值与累积结果组合在一起 . 看到:如果列表为空
foldLeft
可以将初始值显示为合法结果 . 另一方面,如果在列表中找不到至少一个值,则reduceLeft
没有合法值 .作为参考,
reduceLeft
如果应用于具有以下错误的空容器将会出错 .重新编写要使用的代码
是一个潜在的选择 . 另一种方法是使用
reduceLeftOption
变量返回Option包装结果 .它们都在Scala标准库中的基本原因可能是因为它们都在Haskell标准库中(称为
foldl
和foldl1
) . 如果reduceLeft
不是,则通常将其定义为不同项目中的便捷方法 .来自Functional Programming Principles in Scala(Martin Odersky):
[与
reduceLeft
相对,在空列表中调用时抛出异常 . ]该课程(参见讲座5.5)提供了这些函数的抽象定义,这些定义说明了它们之间的差异,尽管它们在模式匹配和递归的使用方面非常相似 .
请注意,
foldLeft
返回U
类型的值,该类型的类型不一定与List[T]
相同,但reduceLeft返回与列表类型相同的值 .要真正理解你在使用fold / reduce做什么,请检查:http://wiki.tcl.tk/17983非常好的解释 . 一旦你得到折叠的概念,reduce将与上面的答案一起:list.tail.foldLeft(list.head)(_)