In generale, tutte e 6 le funzioni di piega applicano un operatore binario a ciascun elemento di una raccolta. Il risultato di ogni passaggio viene passato al passaggio successivo (come input per uno dei due argomenti dell'operatore binario). In questo modo possiamo accumulare un risultato.
reduceLeft
e reduceRight
accumulare un singolo risultato.
foldLeft
e foldRight
accumulare un singolo risultato usando un valore iniziale.
scanLeft
e scanRight
accumulare una raccolta di risultati cumulativi intermedi utilizzando un valore iniziale.
Accumulare
Da SINISTRA e in avanti ...
Con una raccolta di elementi abc
e un operatore binario add
possiamo esplorare cosa fanno le diverse funzioni di piega quando si procede in avanti dall'elemento SINISTRA della raccolta (dalla A alla 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
Da DESTRA e indietro ...
Se iniziamo con l'elemento DESTRA e torniamo indietro (da C ad A) noteremo che ora il secondo argomento per il nostro operatore binario accumula il risultato (l'operatore è lo stesso, abbiamo semplicemente cambiato i nomi degli argomenti per chiarire i loro ruoli ):
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-cumulare
Da SINISTRA e in avanti ...
Se invece dovessimo cumulare alcuni risultati per sottrazione a partire dall'elemento LEFT di una raccolta, accumuleremmo il risultato attraverso il primo argomento res
del nostro operatore 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)
Da DESTRA e indietro ...
Ma cerca subito le varianti di xRight! Ricorda che il valore (de) cumulato nelle variazioni di xRight viene passato al secondo parametro res
del nostro operatore 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)
L'ultimo elenco (-2, 3, -1, 4, 0) non è forse quello che ti aspetteresti intuitivamente!
Come vedi, puoi controllare cosa sta facendo il tuo foldX semplicemente eseguendo invece scanX ed eseguendo il debug del risultato cumulativo ad ogni passaggio.
Linea di fondo