197 votes

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

Quand devrais-je utiliser reduceLeft , reduceRight , foldLeft , foldRight , scanLeft ou scanRight ?

Je veux une intuition / aperçu de leurs différences - éventuellement avec quelques exemples simples.

387voto

Marc Grue Points 1092

En général, tous les 6 fois les fonctions d'appliquer un opérateur binaire à chaque élément d'une collection. Le résultat de chaque étape est passée à l'étape suivante (en tant qu'entrée à l'un des opérateur binaire de deux arguments). De cette façon, nous pouvons cumuler un résultat.

reduceLeft et reduceRight cumuler un résultat unique.

foldLeft et foldRight cumuler un résultat unique à l'aide d'une valeur de départ.

scanLeft et scanRight cumuler une collection d'intermédiaires résultats cumulés à l'aide d'une valeur de départ.

Accumuler

À partir de la GAUCHE et vers l'avant...

Avec une collection d'éléments abc et un opérateur binaire add nous pouvons explorer les différentes fois les fonctions de faire lorsque l'on va vers l'avant de la GAUCHE de l'élément 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 la DROITE et vers l'arrière...

Si nous commençons avec le DROIT de l'élément et de revenir en arrière (de C à A) on remarquera que, maintenant, la deuxième argument à notre opérateur binaire s'accumule le résultat (l'opérateur est le même, nous avons juste changé le nom des arguments à faire leurs rôles clair):

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

À partir de la GAUCHE et vers l'avant...

Si, au contraire, nous avons été à de cumuler un résultat par soustraction, en partant de la GAUCHE élément d'une collection, il nous serait possible de cumuler le résultat par 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 la DROITE et vers l'arrière...

Mais regarder dehors pour le xRight variations maintenant! Rappelez-vous que la (-) valeur cumulée dans le xRight variations est passé à la 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) 

Le dernier de la Liste(-2, 3, -1, 4, 0) est peut-être pas ce que vous attendez!

Comme vous le voyez, vous pouvez vérifier que votre foldX est fait par la simple exécution d'un scanX au lieu et déboguer le résultat cumulé à chaque étape.

Bas de ligne

  • Cumuler un résultat avec reduceLeft ou reduceRight.
  • Cumuler un résultat avec foldLeft ou foldRight si vous avez une valeur de départ.
  • Cumuler une collection de résultats intermédiaires avec scanLeft ou scanRight.

  • Utiliser un xLeft variation si vous voulez aller transmet par le biais de la collection.

  • Utiliser un xRight variation si vous voulez aller vers l'arrière à travers la collection.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X