Skip to content

Instantly share code, notes, and snippets.

@coproduto
Last active June 6, 2024 03:50
Show Gist options
  • Select an option

  • Save coproduto/5550b80f96dfaebc7446eb2e7c82dc11 to your computer and use it in GitHub Desktop.

Select an option

Save coproduto/5550b80f96dfaebc7446eb2e7c82dc11 to your computer and use it in GitHub Desktop.
/* A função reduce é capaz de representar uma grande quantidade de loops imperativos.
* Por que isso é possível? Reduce não é só pra extrair um valor primitivo de um array?
*
* Bom, não exatamente. Na verdade, o reduce *encapsula* uma lógica que representa muitos
* dos loops que fazemos.
*
* Os argumentos do reduce são:
* * Um array de algum tipo Elem de elementos;
* * Um acumulador inicial de algum tipo Acc;
* * Uma função que recebe um acumulador (tipo Acc), um elemento (tipo Elem) e retorna um novo acumulador;
* Podemos representar essa função como Acc -> Elem -> Acc. Chamaremos esta função de agora em diante de
* "função redutora".
*
* Quando pensamos no reduce como "resumindo" um array, vemos o acumulador como o agregado atual e a função redutora
* como atualizando o agregado com um elemento do array. Então:
*/
const somaArray = (array) =>
array.reduce((somaAtual, novoElemento) => somaAtual + novoElemento, 0);
// notar que em JS, o reduce é chamado como método do array,
// a função redutora é o primeiro argumento e o acumulador inicial o segundo.
/* No exemplo acima, a função redutora atualiza o acumulador, que começa em 0, somando cada elemento a ele.
* A maioria dos casos em que usamos "reduce" para "resumir" um array tem esse formato. Mas podemos ver o reduce
* de uma forma diferente.
*
* Podemos ver o primeiro acumulador como o *estado inicial* de um processo iterativo;
* Podemos ver a função redutora como *atualizando o estado* com cada elemento novo.
* Assim, podemos implementar a função que retorna os n primeiros números de Fibonacci com um loop da seguinte forma:
*/
const sequenciaFibonacci = (n) => {
let contador = n;
let anterior = 0;
let atual = 1;
let resultados = []
while (contador > 0) {
const novo = anterior + atual;
resultados.push(atual);
anterior = atual;
atual = novo;
contador--;
}
return resultados;
};
/* Perceba que o estado da nossa iteração tem três variáveis: "atual", "anterior" e "resultados", enquanto o
* contador é utilizado para saber quantas vezes devemos repetir a iteração.
*
* Para fazer a mesma coisa utilizando um reduce, podemos armazenar essas três variáveis no estado da recursão:
*/
const sequenciaFibonacciReduce = (n) => {
// por peculiaridade do JS, se queremos gerar um array de n elementos e não ligamos
// pra quais os elementos sejam, precisamos fazer como abaixo:
const sequencia = Array(n).fill(undefined);
const estadoInicial = {
anterior: 0,
atual: 1,
resultados: [],
};
const atualizaEstado = (estadoPrevio) => ({
anterior: estadoPrevio.atual,
atual: estadoPrevio.anterior + estadoPrevio.atual,
resultados: estadoPrevio.resultados.concat(estadoPrevio.atual),
});
return sequencia.reduce(atualizaEstado, estadoInicial).resultados;
}
/* Então percebam: Convertemos o estado da iteração em uma estrutura de dados e a iteração em si
* em uma função atualizadora do estado, e geramos uma sequência que garante que a iteração seja executada
* o número correto de vezes.
*
* Em quase todo loop imperativo, pensamos em um estado sendo atualizado em um número de iterações dado ou
* por um número ou por uma estrutura de dados.
*
* A função reduce encapsula essa lógica de ter um estado que é atualizado um determinado número de vezes,
* recebendo explicitamente um estado inicial e um "atualizador".
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment