Estoy bastante confundido con estas dos funciones fold()
y reduce()
en Kotlin, ¿alguien puede darme un ejemplo concreto que las distinga a ambas?
Estoy bastante confundido con estas dos funciones fold()
y reduce()
en Kotlin, ¿alguien puede darme un ejemplo concreto que las distinga a ambas?
Respuestas:
fold
toma un valor inicial, y la primera invocación de la lambda que le pase recibirá ese valor inicial y el primer elemento de la colección como parámetros.
Por ejemplo, tome el siguiente código que calcula la suma de una lista de enteros:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
La primera llamada a la lambda será con parámetros 0
y 1
.
Tener la capacidad de pasar un valor inicial es útil si tiene que proporcionar algún tipo de valor o parámetro predeterminado para su operación. Por ejemplo, si estaba buscando el valor máximo dentro de una lista, pero por alguna razón desea devolver al menos 10, puede hacer lo siguiente:
listOf(1, 6, 4).fold(10) { max, element ->
if (element > max) element else max
}
reduce
no toma un valor inicial, sino que comienza con el primer elemento de la colección como el acumulador (llamado sum
en el siguiente ejemplo).
Por ejemplo, hagamos una suma de enteros nuevamente:
listOf(1, 2, 3).reduce { sum, element -> sum + element }
La primera llamada a la lambda aquí será con parámetros 1
y 2
.
Puede usarlo reduce
cuando su operación no dependa de ningún valor distinto de los de la colección a la que lo está aplicando.
emptyList<Int>().reduce { acc, s -> acc + s }
producirá una excepción, pero emptyList<Int>().fold(0) { acc, s -> acc + s }
está bien.
listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }
(el tipo de lista es Int, mientras que el tipo de acumulador se declara como Number y en realidad es Long)
La principal diferencia funcional que llamaría (que se menciona en los comentarios en la otra respuesta, pero puede ser difícil de entender) es que reduce
arrojará una excepción si se realiza en una colección vacía.
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
Esto se debe a .reduce
que no sabe qué valor devolver en caso de "sin datos".
Compare esto con .fold
, que requiere que proporcione un "valor inicial", que será el valor predeterminado en el caso de una colección vacía:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
Entonces, incluso si no desea agregar su colección a un solo elemento de un tipo diferente (no relacionado) (que solo .fold
le permitirá hacerlo), si su colección inicial puede estar vacía, entonces debe verificar su colección tamaño primero y luego .reduce
, o simplemente use.fold
val collection: List<Int> = // collection of unknown size
val result1 = if (collection.isEmpty()) 0
else collection.reduce { x, y -> x + y }
val result2 = collection.fold(0) { x, y -> x + y }
assertEquals(result1, result2)