web-dev-qa-db-fra.com

Réduire, plier ou numériser (gauche / droite)?

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.

171
Marc Grue

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.

Accumuler

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)

.

Décumuler

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.

Ligne de fond

  • Cumuler un résultat avec reduceLeft ou reduceRight.
  • Cumulez un résultat avec 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.

  • Utilisez une variante xRight si vous souhaitez revenir dans la collection.
351
Marc Grue

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.

  • La méthode LEFT_REDUCE fonctionne de manière similaire à la méthode REDUCE.
  • 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")

  • {A, B} => AB {AB, C} => ABC {ABC, D} => ABCD {ABCD, E} => ABCDE réduire (a + b) ABCDE
  • {A, B} => AB {AB, C} => ABC {ABC, D} => ABCD {ABCD, E} => ABCDE réduireLeft (a + b) ABCDE
  • {A, B} => BA {BA, C} => CBA {CBA, D} => DCBA {DCBA, E} => EDCBA réduireLeft (b + a) EDCB
  • {D, E} => DE {C, DE} => CDE {B, CDE} => BCDE {A, BCDE} => ABCDE réduireDroit (a + b) ABCDE
  • {D, E} => ED {C, ED} => EDC {B, EDC} => EDCB {A, EDCB} => EDCBA réduire à droite (b + a) EDCBA

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}))
}
8
Puneeth Reddy V