缩小、折叠或扫描(左/右)? [英] Reduce, fold or scan (Left/Right)?
问题描述
我应该什么时候使用reduceLeft
、reduceRight
、foldLeft
、foldRight
、scanLeft
> 还是 scanRight
?
When should I use reduceLeft
, reduceRight
, foldLeft
, foldRight
, scanLeft
or scanRight
?
我想对它们的差异有一个直观的/概述 - 可能是一些简单的例子.
I want an intuition/overview of their differences - possibly with some simple examples.
推荐答案
一般来说,所有 6 折函数都将二元运算符应用于集合的每个元素.每个步骤的结果传递到下一步(作为二元运算符的两个参数之一的输入).这样我们就可以累积一个结果.
In general, all 6 fold functions apply a binary operator to each element of a collection. The result of each step is passed on to the next step (as input to one of the binary operator's two arguments). This way we can cumulate a result.
reduceLeft
和 reduceRight
累积一个结果.
reduceLeft
and reduceRight
cumulate a single result.
foldLeft
和 foldRight
使用起始值累积单个结果.
foldLeft
and foldRight
cumulate a single result using a start value.
scanLeft
和 scanRight
使用起始值累积中间累积结果的集合.
scanLeft
and scanRight
cumulate a collection of intermediate cumulative results using a start value.
从左到右...
使用元素集合 abc
和二元运算符 add
,我们可以探索不同的折叠函数在从集合的 LEFT 元素(从 AC):
With a collection of elements abc
and a binary operator add
we can explore what the different fold functions do when going forwards from the LEFT element of the collection (from A to 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),我们会注意到现在二元运算符的 second 参数累加了结果(运算符是相同的,我们只是切换了参数名称以明确它们的作用):
If we start with the RIGHT element and go backwards (from C to A) we'll notice that now the second argument to our binary operator accumulates the result (the operator is the same, we just switched the argument names to make their roles clear):
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 元素开始的减法来de-cumulate某个结果,我们将通过我们的第一个参数 res
对结果进行累加.二元运算符减
:
If instead we were to de-cumulate some result by subtraction starting from the LEFT element of a collection, we would cumulate the result through the first argument res
of our binary operator 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 变量中的(去)累积值会传递给我们的二元运算符 minus
的 second 参数 res
:
But look out for the xRight variations now! Remember that the (de-)cumulated value in the xRight variations is passed to the second parameter res
of our binary operator 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) 可能不是你直觉所期望的!
The last List(-2, 3, -1, 4, 0) is maybe not what you would intuitively expect!
如您所见,您可以通过简单地运行 scanX 来检查您的 foldX 正在做什么,并在每一步调试累积结果.
As you see, you can check what your foldX is doing by simply running a scanX instead and debug the cumulated result at each step.
- 使用
reduceLeft
或reduceRight
累积结果. - 如果您有起始值,则使用
foldLeft
或foldRight
累积结果. 使用
scanLeft
或scanRight
累积中间结果的集合.
- Cumulate a result with
reduceLeft
orreduceRight
. - Cumulate a result with
foldLeft
orfoldRight
if you have a start value. Cumulate a collection of intermediate results with
scanLeft
orscanRight
.
如果您想向前浏览集合,请使用 xLeft 变体.
Use a xLeft variation if you want to go forwards through the collection.
这篇关于缩小、折叠或扫描(左/右)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!