Ter um olhar para este para uma discussão profunda fundamentais deste tópico
GhostCat
2
@LunarWatcher, eu vi esses documentos, mas não consegui-lo, isso é uma pergunta postada, você pode dar um exemplo?
precisa saber é o seguinte
1
@MattKlein done
Jayson Minard
Respostas:
281
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.
Boa explicação! Eu diria também que a coleção vazia não pode ser reduzida, mas pode ser dobrada.
Miha_x64
veja, no nível iniciante do Kotlin, o primeiro exemplo que você deu pode explicar mais com alguns passos e a resposta final? seria uma grande ajuda
TapanHP
3
O @TapanHP emptyList<Int>().reduce { acc, s -> acc + s }produzirá uma exceção, mas emptyList<Int>().fold(0) { acc, s -> acc + s }está OK.
Miha_x64
31
reduzir também força o retorno do lambda a ser do mesmo tipo que os membros da lista, o que não ocorre com fold. Essa é uma consequência importante de tornar o primeiro elemento da lista, o valor inicial do acumulador.
andresp
4
@andresp: apenas como uma observação para completar: ele não precisa ser do mesmo tipo. Os membros da lista também pode ser um subtipo do acumulador: isso não funciona 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)
Boris
11
A principal diferença funcional que eu chamaria (mencionada nos comentários da outra resposta, mas pode ser difícil de entender) é que reducelanç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 sizeval result1=if(collection.isEmpty())0else collection.reduce { x, y -> x + y }val result2= collection.fold(0){ x, y -> x + y }
assertEquals(result1, result2)
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:
A primeira chamada para o lambda será com parâmetros
0
e1
.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:
reduce
não assume um valor inicial, mas começa com o primeiro elemento da coleção como acumulador (chamadosum
no exemplo a seguir).Por exemplo, vamos fazer uma soma de números inteiros novamente:
A primeira chamada para o lambda aqui será com os parâmetros
1
e2
.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.fonte
emptyList<Int>().reduce { acc, s -> acc + s }
produzirá uma exceção, masemptyList<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.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: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
fonte