Fold ou Reduce em Kotlin: a eterna dúvida dos iniciantes
\n\nSe você programa em Kotlin há pelo menos uma semana, certamente já se deparou com as funções fold e reduce. À primeira vista, elas fazem a mesma coisa: percorrem uma coleção e acumulam um resultado. Mas o diabo, como sempre, está nos detalhes. A escolha errada pode levar a bugs, exceções ou simplesmente a um código feio.
Neste artigo, vamos analisar a diferença entre fold e reduce em Kotlin, ver exemplos práticos de projetos reais e aprender de uma vez por todas quando usar cada função. Vamos lá!
\n\nO que são fold e reduce? Entendimento básico
\n\nAmbas as funções são operações terminais para coleções. Elas pegam todos os elementos e os transformam em um único valor. Imagine que você tem uma pilha de panquecas e quer fazer um único hambúrguer grande. fold e reduce são duas técnicas diferentes de empilhar.
Reduce: abordagem minimalista
\n\nA função reduce usa o primeiro elemento da coleção como valor inicial do acumulador. Isso significa que ela simplesmente não pode trabalhar com uma coleção vazia — lançará uma UnsupportedOperationException.
val numbers = listOf(1, 2, 3, 4, 5)\nval sum = numbers.reduce { acc, i -> acc + i }\nprintln(sum) // 15\n\nAqui, acc é inicialmente igual a 1 (primeiro elemento), depois soma-se 2, depois 3 e assim por diante. Simples e elegante, mas há um detalhe.
Fold: controle sobre o início
\n\nfold, ao contrário de reduce, exige que você especifique explicitamente um valor inicial. Isso lhe dá controle total e, mais importante, permite trabalhar com coleções vazias.
val numbers = listOf(1, 2, 3, 4, 5)\nval sum = numbers.fold(0) { acc, i -> acc + i }\nprintln(sum) // 15\n\nval emptyList = listOf<Int>()\nval emptySum = emptyList.fold(0) { acc, i -> acc + i }\nprintln(emptySum) // 0, nenhum erro!\n\nPercebe a diferença? fold retorna o valor inicial se a coleção estiver vazia. reduce falharia nessa situação.
Principais diferenças entre fold e reduce em Kotlin
\n\nVamos sistematizar o conhecimento. Aqui está uma tabela para ajudar você a se orientar rapidamente:
\n\n- \n
- Valor inicial: Em
fold, você define ele mesmo. Emreduce, é o primeiro elemento da coleção. \n - Tipo do resultado: Em
fold, o tipo do resultado pode ser diferente do tipo dos elementos (por exemplo, somar Int e obter String). Emreduce, o tipo do resultado é sempre o mesmo tipo dos elementos. \n - Coleção vazia:
foldlida normalmente (retorna o valor inicial).reducelança uma exceção. \n - Desempenho: Ambas as funções operam em O(n). A diferença de desempenho é imperceptível. \n
Agora, vamos ver isso no código.
\n\nExemplo 1: Tipos diferentes de acumulador e elementos
\n\nSuponha que temos uma lista de números e queremos obter uma string — a soma deles em formato legível. fold funciona perfeitamente, pois o tipo do acumulador (String) é diferente do tipo dos elementos (Int).
val numbers = listOf(1, 2, 3)\nval result = numbers.fold("Soma: ") { acc, i -> "$acc$i " }\nprintln(result) // "Soma: 1 2 3 "\n\nTente fazer o mesmo com reduce — o compilador emitirá um erro, porque reduce exige que o tipo do acumulador seja igual ao tipo dos elementos.
Exemplo 2: Cálculo de soma com valor inicial
\n\nUma tarefa comum: calcular o total de pedidos, mas já temos um saldo inicial na conta.
\n\nval orders = listOf(100, 250, 75)\nval initialBalance = 500\nval totalBalance = orders.fold(initialBa