En general, las 6 funciones de plegado aplican un operador binario a cada elemento de una colección. El resultado de cada paso se pasa al siguiente paso (como entrada a uno de los dos argumentos del operador binario). De esta manera podemos acumular un resultado.
reduceLeft
y reduceRight
acumular un solo resultado.
foldLeft
y foldRight
acumular un solo resultado utilizando un valor inicial.
scanLeft
y scanRight
acumular una colección de resultados acumulativos intermedios utilizando un valor inicial.
Acumular
De izquierda a derecha ...
Con una colección de elementos abc
y un operador binario, add
podemos explorar lo que hacen las diferentes funciones de plegado al avanzar desde el elemento IZQUIERDO de la colección (de A 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
Desde la DERECHA y hacia atrás ...
Si comenzamos con el elemento DERECHO y retrocedemos (de C a A), notaremos que ahora el segundo argumento para nuestro operador binario acumula el resultado (el operador es el mismo, solo cambiamos los nombres de los argumentos para aclarar sus roles ):
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)
.
Desacumular
De izquierda a derecha ...
Si, en cambio, dejáramos de acumular algún resultado restando a partir del elemento IZQUIERDO de una colección, acumularíamos el resultado a través del primer argumento res
de nuestro operador binario 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)
Desde la DERECHA y hacia atrás ...
¡Pero busque las variaciones de xRight ahora! Recuerde que el valor (des) acumulado en las variaciones xRight se pasa al segundo parámetro res
de nuestro operador binario 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)
¡La última Lista (-2, 3, -1, 4, 0) tal vez no sea lo que intuitivamente esperaría!
Como puede ver, puede verificar qué está haciendo su foldX simplemente ejecutando un scanX en su lugar y depurar el resultado acumulado en cada paso.
Línea de fondo