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 0e 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
}
reducenão assume um valor inicial, mas começa com o primeiro elemento da coleção como acumulador (chamado sumno 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 1e 2.
Você pode usar reducequando 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 .reducenã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ó .foldpermitirá), 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)