Reducir, doblar o escanear (Izquierda/Derecha)?


Cuando debo usar reduceLeft, reduceRight, foldLeft, foldRight, scanLeft o scanRight?

Quiero una intuición/visión general de sus diferencias - posiblemente con algunos ejemplos simples.

Author: Marc Grue, 2013-07-01

2 answers

En general, todas las 6 funciones fold aplican un operador binario a cada elemento de una colección. El resultado de cada paso se pasa al siguiente paso (como entrada a uno de los dos argumentos del operador binario). De esta manera podemos acumular un resultado.

reduceLeft y reduceRight acumulan un solo resultado.

foldLeft y foldRight acumulan un solo resultado usando un valor inicial.

scanLeft and scanRight cumulate a collection of intermediate cumulative results using a start valor.

Acumular

De IZQUIERDA a derecha...

Con una colección de elementos abc y un operador binario add podemos explorar lo que hacen las diferentes funciones de plegado cuando se avanza desde el elemento IZQUIERDO de la colección (de A 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 DERECHA y de atrás...

Si empezamos con el elemento DERECHO y vamos hacia atrás (de C a A) nos daremos cuenta de que ahora el segundo argumento a nuestra el operador binario acumula el resultado (el operador es el mismo, solo cambiamos los nombres de los argumentos para aclarar sus roles):

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)

.

Desacumular

De IZQUIERDA a derecha...

Si en cambio tuviéramos que des-acumular algún resultado por resta comenzando desde el elemento IZQUIERDO de una colección, acumularíamos el resultado a través del primer argumento res de nuestro operador 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)


Desde DERECHA y atrás...

Pero cuidado con las variaciones de xRight ahora! Recuerde que el valor (de-)acumulado en las variaciones xRight se pasa al segundo parámetro res de nuestro operador 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) 

La última Lista(-2, 3, -1, 4, 0) ¡quizás no es lo que intuitivamente esperarías!

Como puede ver, puede comprobar lo que está haciendo su foldX simplemente ejecutando un ScanX en su lugar y depurar el resultado acumulado en cada paso.

Inferior línea

  • Acumular un resultado con reduceLeft o reduceRight.
  • Acumula un resultado con foldLeft o foldRight si tienes un valor inicial.
  • Acumular una colección de resultados intermedios con scanLeft o scanRight.

  • Utilice una variación xLeft si desea ir hacia adelante a través de la colección.

  • Use una variación xRight si desea ir hacia atrás a través de la colección.
 324
Author: Marc Grue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-01 20:03:08

Normalmente REDUCIR,DOBLAR,método de EXPLORACIÓN funciona mediante la acumulación de datos a la IZQUIERDA y seguir cambiando la variable DERECHA. La principal diferencia entre ellos es REDUCIR, DOBLAR es: -

Fold siempre comenzará con un valor seed, es decir, un valor inicial definido por el usuario. Reducir lanzará una excepción si la colección está vacía donde como doblar devuelve el valor de semilla. Siempre dará como resultado un solo valor.

Scan se utiliza para algún orden de procesamiento de elementos desde el lado izquierdo o derecho, entonces podemos hacer uso del resultado anterior en el cálculo posterior. Eso significa que podemos escanear elementos. Siempre resultará una colección.

  • El método LEFT_REDUCE funciona de manera similar al Método REDUCE.
  • RIGHT_REDUCE es opuesto a reduceLeft, es decir, acumula valores en la DERECHA y sigue cambiando la variable izquierda.

  • ReduceLeftOption y reduceRightOption son similares a left_reduce y right_reduce la única diferencia es que devuelven resultados en la OPCIÓN objeto.

Una parte de la salida para el código mencionado a continuación sería: -

Usando scan operación sobre una lista de números (usando seed valor 0) List(-2,-1,0,1,2)

  • {0,-2}=>-2 {-2,-1}=>-3 {-3,0}=>-3 {-3,1}=>-2 {-2,2}=>0 Lista de escaneo(0, -2, -3, -3, -2, 0)

  • {0,-2}=>-2 {-2,-1}=>-3 {-3,0}=>-3 {-3,1}=>-2 {-2,2}=>0 scanLeft (a+b) Lista de(0, -2, -3, -3, -2, 0)

  • {0,-2}=>-2 {-2,-1}=>-3 {-3,0}=>-3 {-3,1}=>-2 {-2,2}=>0 Lista scanLeft (b+a) (0, -2, -3, -3, -2, 0)

  • {2,0}=>2 {1,2}=>3 {0,3}=>3 {-1,3}=>2 {-2,2}=>0 scanRight (a+b) Lista de(0, 2, 3, 3, 2, 0)

  • {2,0}=>2 {1,2}=>3 {0,3}=>3 {-1,3}=>2 {-2,2}=>0 scanRight (b+a) Lista de(0, 2, 3, 3, 2, 0)

Usando reduce,fold operaciones sobre una lista de Cadenas List("A","B","C","D","E")

  • {A,B}=>AB {AB,C}=>ABC {ABC, D}=>ABCD {ABCD, E} = > ABCDE reduce (a + b) ABCDE
  • {A, B}=>AB {AB, C} = > ABC {ABC, D} = > ABCD {ABCD, E} = > ABCDE reduceLeft (a+b) ABCDE
  • {A,B}=>BA {BA,C}=>CBA {CBA, D}=>DCBA {DCBA, E} = > EDCBA reduceLeft (b+a) EDCB
  • {D,E}=>DE {C,DE}=>CDE {B,CDE}=>BCDE {A, BCDE}=>ABCDE reduceRight (a+b) ABCDE
  • {D,E}=>ED {C,ED}=>EDC {B,EDC}=>EDCB {A, EDCB} = > EDCBA reduceRight (b+a) EDCBA

Código:

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
Author: Puneeth Reddy V,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-17 06:36:22