我什么时候应该使用reduceLeft
,reduceRight
,foldLeft
,foldRight
,scanLeft
或scanRight
?
我希望对它们的区别有一个直观的认识/概述-可能有一些简单的例子。
最佳答案
通常,所有6折函数都将二进制运算符应用于集合的每个元素。每个步骤的结果都将传递到下一步(作为对二进制运算符的两个参数之一的输入)。这样我们可以累计结果。reduceLeft
和reduceRight
累积单个结果。foldLeft
和foldRight
使用起始值累计单个结果。scanLeft
和scanRight
使用起始值累计中间累积结果的集合。
积累
从左向前进...
使用元素abc
的集合和二进制运算符add
,我们可以探索从集合的LEFT元素(从A到C)前进时不同的折叠函数的作用:
val abc = List("A", "B", "C")
def add(res: String, x: String) = {
println(s"op: $res + $x = ${res + x}")
res + x
}
abc.reduceLeft(add)
// op: A + B = AB
// op: AB + C = ABC // accumulates value AB in *first* operator arg `res`
// res: String = ABC
abc.foldLeft("z")(add) // with start value "z"
// op: z + A = zA // initial extra operation
// op: zA + B = zAB
// op: zAB + C = zABC
// res: String = zABC
abc.scanLeft("z")(add)
// op: z + A = zA // same operations as foldLeft above...
// op: zA + B = zAB
// op: zAB + C = zABC
// res: List[String] = List(z, zA, zAB, zABC) // maps intermediate results
从右向后...
如果我们从RIGHT元素开始,然后倒退(从C到A),我们会注意到现在二进制运算符的第二个参数会累加结果(该运算符是相同的,我们只是切换了参数名称以使其角色清楚):
def add(x: String, res: String) = {
println(s"op: $x + $res = ${x + res}")
x + res
}
abc.reduceRight(add)
// op: B + C = BC
// op: A + BC = ABC // accumulates value BC in *second* operator arg `res`
// res: String = ABC
abc.foldRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: String = ABCz
abc.scanRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: List[String] = List(ABCz, BCz, Cz, z)
。
去累积
从左向前进...
相反,如果我们要从集合的LEFT元素开始通过减法来对某些结果求反积,那么我们将通过二进制运算符
res
的第一个参数minus
求和结果:val xs = List(1, 2, 3, 4)
def minus(res: Int, x: Int) = {
println(s"op: $res - $x = ${res - x}")
res - x
}
xs.reduceLeft(minus)
// op: 1 - 2 = -1
// op: -1 - 3 = -4 // de-cumulates value -1 in *first* operator arg `res`
// op: -4 - 4 = -8
// res: Int = -8
xs.foldLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: Int = -10
xs.scanLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: List[Int] = List(0, -1, -3, -6, -10)
从右向后...
但是,现在请注意xRight版本!请记住,xRight变体中的(去)累加值被传递给我们的二进制运算符
res
的第二个参数minus
:def minus(x: Int, res: Int) = {
println(s"op: $x - $res = ${x - res}")
x - res
}
xs.reduceRight(minus)
// op: 3 - 4 = -1
// op: 2 - -1 = 3 // de-cumulates value -1 in *second* operator arg `res`
// op: 1 - 3 = -2
// res: Int = -2
xs.foldRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: Int = -2
xs.scanRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: List[Int] = List(-2, 3, -1, 4, 0)
最后一个List(-2、3,-1、4、0)可能不是您直觉期望的!
如您所见,您可以通过简单地运行scanX来检查foldX的功能,并在每个步骤中调试累积的结果。
底线
reduceLeft
或reduceRight
累加结果。 foldLeft
或foldRight
累积结果。 scanLeft
或scanRight
累积中间结果的集合。