En général, les 6 fonctions fold appliquent un opérateur binaire à chaque élément d'une collection. Le résultat de chaque étape est passé à l'étape suivante (comme entrée de l'un des deux arguments de l'opérateur binaire). De cette façon, nous pouvons cumuler un résultat.
reduceLeft
et reduceRight
cumulez un seul résultat.
foldLeft
et foldRight
cumulez un seul résultat en utilisant une valeur de départ.
scanLeft
et scanRight
cumulez une collection de résultats cumulatifs intermédiaires en utilisant une valeur de départ.
Accumuler
De GAUCHE et vers l'avant ...
Avec une collection d'éléments abc
et un opérateur binaire, add
nous pouvons explorer ce que font les différentes fonctions de repli lors du passage de l'élément LEFT de la collection (de 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
De DROITE et en arrière ...
Si nous commençons par l'élément RIGHT et revenons en arrière (de C à A), nous remarquerons que maintenant le deuxième argument de notre opérateur binaire accumule le résultat (l'opérateur est le même, nous avons simplement changé les noms des arguments pour clarifier leurs rôles ):
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)
.
De-cumuler
De GAUCHE et vers l'avant ...
Si à la place nous devions décumuler un résultat par soustraction à partir de l'élément LEFT d'une collection, nous cumulions le résultat via le premier argument res
de notre opérateur binaire 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)
De DROITE et en arrière ...
Mais recherchez les variations xRight maintenant! Rappelez-vous que la valeur (dé-) cumulée dans les variations xRight est passée au deuxième paramètre res
de notre opérateur binaire 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 dernière liste (-2, 3, -1, 4, 0) n'est peut-être pas ce à quoi vous vous attendriez intuitivement!
Comme vous le voyez, vous pouvez vérifier ce que fait votre foldX en exécutant simplement un scanX à la place et déboguer le résultat cumulé à chaque étape.
En bout de ligne