Reduce es una función fundamental en la programación funcional que nos permite transformar una lista o cualquier enumerable en un único valor. A diferencia de Filter y Map, que mantienen la estructura de la colección devolviendo otra lista o enumerable, Reduce condensa todos los elementos en un solo resultado, gracias a un mecanismo llamado acumulador.
Para entender cómo funciona, imaginemos que tenemos una lista con los números del 1 al 5. Reduce recibe esta lista, una función y un valor inicial para el acumulador. La función que le pasamos debe aceptar dos parámetros: el elemento actual de la lista y el acumulador. En la primera llamada, el acumulador es el valor inicial que definimos, por ejemplo, 0. La función procesa el primer elemento y el acumulador, y devuelve un nuevo valor que será el acumulador para la siguiente iteración. Así, para el segundo elemento, la función recibe ese nuevo acumulador y el siguiente elemento, y así sucesivamente hasta procesar toda la lista.
Si la función que definimos suma el elemento actual con el acumulador, el proceso sería así: primero suma 1 + 0, dando 1; luego 2 + 1, que es 3; después 3 + 3, que da 6; luego 4 + 6, que es 10; y finalmente 5 + 10, que resulta en 15. Al terminar, Reduce devuelve ese acumulador final, en este caso 15, que es la suma de todos los elementos.
Este concepto puede parecer un poco abstracto al principio, especialmente por el orden de los parámetros y el papel del acumulador, pero es una herramienta muy poderosa. No solo sirve para sumar, sino que podemos usarla para multiplicar, concatenar, o incluso construir estructuras más complejas.
Por ejemplo, podemos implementar nuestra propia versión de una función que convierte una lista de tuplas en un mapa. Supongamos que tenemos una lista de cadenas como [hola, adiós, buenas tardes] y queremos crear un mapa donde cada cadena sea la clave y su longitud el valor. Primero, podríamos usar map con string.length para obtener las longitudes, pero eso nos daría una lista de números, no un mapa.
Aquí es donde Reduce brilla. Podemos iniciar el acumulador como un mapa vacío y, para cada cadena, actualizar el mapa agregando una entrada con la cadena como clave y su longitud como valor. La función que pasamos a Reduce recibe la cadena y el mapa actual, y devuelve un nuevo mapa con la entrada añadida. Como los mapas son inmutables, cada llamada a map.put devuelve una copia actualizada del mapa, sin modificar el original.
El código para esta operación sería algo así:
lista = ["hola", "adiós", "buenas tardes"]
resultado = Enum.reduce(lista, %{}, fn cadena, mapa ->
Map.put(mapa, cadena, String.length(cadena))
end)
Al finalizar, resultado contendrá el mapa %{"hola" => 4, "adiós" => 5, "buenas tardes" => 13}.
Para entender mejor cómo se construye este mapa paso a paso, podemos usar una función como inspect que imprime el estado actual del acumulador sin alterar el flujo de datos, ya que devuelve el mismo valor que recibe. Esto nos permite visualizar el proceso de acumulación sin interferir con la lógica.
Aunque Reduce puede parecer complicado al principio, especialmente por la gestión del acumulador y el orden de los parámetros, con práctica se vuelve una herramienta indispensable para transformar colecciones en valores únicos o estructuras personalizadas. Además, nombrar las variables de forma descriptiva, como cadena y mapa en lugar de genéricos x y acc, ayuda mucho a la comprensión del código.
En definitiva, Reduce nos abre un mundo de posibilidades para manipular y transformar datos de manera elegante y funcional. Y si queremos que nuestro código sea aún más claro y limpio, hay técnicas y patrones que podemos explorar para mejorar la legibilidad y expresividad de nuestras funciones con Reduce.