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.
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
oreduceRight
. - Acumula un resultado con
foldLeft
ofoldRight
si tienes un valor inicial. Acumular una colección de resultados intermedios con
scanLeft
oscanRight
.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.
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}))
}
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