我应该什么时候使用reduceLeft, reduceRight, foldLeft, foldRight,scanLeft或scanRight?
reduceLeft
reduceRight
foldLeft
foldRight
scanLeft
scanRight
我想要对它们的差异有一个直觉/概述——可能有一些简单的例子。
通常,所有 6 折函数都将二元运算符应用于集合的每个元素。每一步的结果被传递到下一步(作为二元运算符的两个参数之一的输入)。这样我们就可以 累积 一个结果。
reduceLeft并reduceRight累积一个结果。
foldLeft并foldRight使用起始值累积单个结果。
scanLeft并scanRight使用起始值累积中间累积结果的集合。
从左到右…
使用元素集合abc和二元运算符add,我们可以探索从集合的 LEFT 元素(从 A 到 C)前进时不同折叠函数的作用:
abc
add
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:
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
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 正在做什么,并在每个步骤中调试累积的结果。
scanLeft用或累积中间结果的集合scanRight。
如果您想继续浏览集合,请使用 xLeft变体。
如果要向 后 浏览集合,请使用 xRight 变体。