Estou bastante confuso com essas duas funções fold()
e reduce()
no Kotlin, alguém pode me dar um exemplo concreto que distinga as duas?
Estou bastante confuso com essas duas funções fold()
e reduce()
no Kotlin, alguém pode me dar um exemplo concreto que distinga as duas?
Respostas:
fold
recebe um valor inicial e a primeira chamada do lambda que você passar para ele receberá esse valor inicial e o primeiro elemento da coleção como parâmetros.
Por exemplo, considere o seguinte código que calcula a soma de uma lista de números inteiros:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
A primeira chamada para o lambda será com parâmetros 0
e 1
.
Ter a capacidade de transmitir um valor inicial é útil se você precisar fornecer algum tipo de valor ou parâmetro padrão para sua operação. Por exemplo, se você estava procurando o valor máximo dentro de uma lista, mas por algum motivo deseja retornar pelo menos 10, você pode fazer o seguinte:
listOf(1, 6, 4).fold(10) { max, element ->
if (element > max) element else max
}
reduce
não assume um valor inicial, mas começa com o primeiro elemento da coleção como acumulador (chamado sum
no exemplo a seguir).
Por exemplo, vamos fazer uma soma de números inteiros novamente:
listOf(1, 2, 3).reduce { sum, element -> sum + element }
A primeira chamada para o lambda aqui será com os parâmetros 1
e 2
.
Você pode usar reduce
quando sua operação não depende de nenhum outro valor além daqueles da coleção em que você está aplicando.
emptyList<Int>().reduce { acc, s -> acc + s }
produzirá uma exceção, mas emptyList<Int>().fold(0) { acc, s -> acc + s }
está OK.
listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }
(a lista do tipo é Int enquanto o tipo acumulador é declarado como Número e, na verdade, é uma Long)
A principal diferença funcional que eu chamaria (mencionada nos comentários da outra resposta, mas pode ser difícil de entender) é que reduce
lançará uma exceção se executada em uma coleção vazia.
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
Isso ocorre porque .reduce
não sabe qual valor retornar no caso de "sem dados".
Compare isso com .fold
, que exige que você forneça um "valor inicial", que será o valor padrão no caso de uma coleção vazia:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
Portanto, mesmo que você não queira agregar sua coleção a um único elemento de um tipo diferente (não relacionado) (o que só .fold
permitirá), se a coleção inicial estiver vazia, verifique a coleção tamanho primeiro e depois .reduce
, ou apenas 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)