Quand devrais-je utiliser reduceLeft
, reduceRight
, foldLeft
, foldRight
, scanLeft
ou scanRight
?
Je veux un aperçu/aperçu de leurs différences - éventuellement avec quelques exemples simples.
En général, les 6 fonctions de pliage appliquent un opérateur binaire à chaque élément d'une collection. Le résultat de chaque étape est transmis à l'étape suivante (en tant qu'entrée dans l'un des deux arguments de l'opérateur binaire). De cette façon, nous pouvons cumuler un résultat.
reduceLeft
et reduceRight
cumulent un seul résultat.
foldLeft
et foldRight
cumulent un seul résultat en utilisant une valeur de départ.
scanLeft
et scanRight
cumulent une collection de résultats cumulatifs intermédiaires en utilisant une valeur de départ.
de GAUCHE et en 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 pliage lors du passage à l'avant 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 à l'envers ...
Si nous commençons avec l'élément RIGHT et que nous reculons (de C en A), nous remarquerons que l'argument de deuxième de notre opérateur binaire accumule le résultat. (l'opérateur est le même, nous avons juste changé les noms d'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 GAUCHE et en avant ...
Si à la place nous devions décomposer un résultat par soustraction à partir de l'élément LEFT d'une collection, nous cumulerions 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 à l'envers ...
Mais attention aux variations de xRight maintenant! Rappelez-vous que la valeur (dé) cumulée dans les variations xRight est transmise au paramètre second 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 et en déboguant le résultat cumulé à chaque étape.
reduceLeft
ou reduceRight
.foldLeft
ou foldRight
si vous avez une valeur de départ.Cumulez une collection de résultats intermédiaires avec scanLeft
ou scanRight
.
Utilisez une variante xLeft si vous voulez aller en avant dans la collection.
Normalement, la méthode REDUCE, FOLD, SCAN fonctionne en accumulant des données sur LEFT et en modifiant continuellement la variable RIGHT. La principale différence entre eux est REDUCE, FOLD est: -
Le pli commencera toujours par une valeur seed
, c'est-à-dire une valeur de départ définie par l'utilisateur. Réduire lève une exception si la collection est vide, où repli rend la valeur de départ. donnera toujours une valeur unique.
Le balayage est utilisé pour certains ordres de traitement des éléments de gauche ou de droite, nous pouvons utiliser les résultats précédents pour les calculs ultérieurs. Cela signifie que nous pouvons numériser des éléments. donnera toujours une collection.
RIGHT_REDUCE est l’opposé pour réduire le plus à gauche, c’est-à-dire qu’il accumule les valeurs dans RIGHT et continue à modifier la variable de gauche.
reductionLeftOption et reductionRightOption sont similaires à left_reduce et right_reduce ne diffèrent que par le fait qu'elles renvoient les résultats dans l'objet OPTION.
Une partie de la sortie pour le code mentionné ci-dessous serait: -
en utilisant scan
l'opération sur une liste de nombres (en utilisant seed
valeur 0
) List(-2,-1,0,1,2)
{0, -2} => - 2 {-2, -1} => - 3 {-3,0} => - 3 {-3,1} => - 2 {-2,2} => 0 Liste de balayage (0, -2, -3, -3, -2, 0)
{0, -2} => - 2 {-2, -1} => - 3 {-3,0} => - 3 {-3,1} => - 2 {-2,2} => 0 scanLeft (a + b) List (0, -2, -3, -3, -2, 0)
{0, -2} => - 2 {-2, -1} => - 3 {-3,0} => - 3 {-3,1} => - 2 {-2,2} => 0 scanLeft (b + a) liste (0, -2, -3, -3, -2, 0)
{2,0} => 2 {1,2} => 3 {0,3} => 3 {-1,3} => 2 {-2,2} => 0 scanRight (a + b) Liste ( 0, 2, 3, 3, 2, 0)
{2,0} => 2 {1,2} => 3 {0,3} => 3 {-1,3} => 2 {-2,2} => 0 scanRight (b + a) Liste ( 0, 2, 3, 3, 2, 0)
utilisation de reduce
, fold
opérations sur une liste de chaînes List("A","B","C","D","E")
Code:
object ScanFoldReduce extends App {
val list = List("A","B","C","D","E")
println("reduce (a+b) "+list.reduce((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (a+b) "+list.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (b+a) "+list.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println("reduceRight (a+b) "+list.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("reduceRight (b+a) "+list.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" ")
b+a
}))
println("scan "+list.scan("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (a+b) "+list.scanLeft("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (b+a) "+list.scanLeft("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println("scanRight (a+b) "+list.scanRight("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanRight (b+a) "+list.scanRight("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
//Using numbers
val list1 = List(-2,-1,0,1,2)
println("reduce (a+b) "+list1.reduce((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (a+b) "+list1.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (b+a) "+list1.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println(" reduceRight (a+b) "+list1.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println(" reduceRight (b+a) "+list1.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" ")
b+a
}))
println("scan "+list1.scan(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (a+b) "+list1.scanLeft(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (b+a) "+list1.scanLeft(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println("scanRight (a+b) "+list1.scanRight(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b}))
println("scanRight (b+a) "+list1.scanRight(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
b+a}))
}