Uso de la función reduce

La función reduce del módulo Enum sirve para acumular parcialmente cada uno de los elementos de una colección enumerada entrante, hasta evaluarse completamente a un único resultado final.

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.

Lista de reproducción
  1. 1
    ¿Qué es Elixir?
    10 minutos
  2. 2
    Instalación de Elixir
    9 minutos
  3. 3
    ¿Qué es la programación funcional? (Como la de Elixir)
    20 minutos
  4. 4
    ¿Cómo funciona la REPL de Elixir?
    7 minutos
  5. 5
    ¿Cómo hacer asignaciones en Elixir?
    7 minutos
  6. 6
    Operadores aritméticos básicos
    6 minutos
  7. 7
    ¿Qué son los tipos de datos de Elixir?
    5 minutos
  8. 8
    Átomos en Elixir
    4 minutos
  9. 9
    Las palabras clave nil, true y false
    6 minutos
  10. 10
    Operadores lógicos de comparación
    8 minutos
  11. 11
    Comparación estricta con ===
    3 minutos
  12. 12
    Operadores lógicos y proposicionales
    8 minutos
  13. 13
    Invocación de funciones
    10 minutos
  14. 14
    Fundamentos de funciones
    9 minutos
  15. 15
    Cadenas de caracteres
    8 minutos
  16. 16
    Entrada y salida estandar de la mano de gets y puts
    9 minutos
  17. 17
    Concatenar e interpolar strings
    9 minutos
  18. 18
    Código fuente en archivos
    9 minutos
  19. 19
    Condicional IF y bloques DO-END
    11 minutos
  20. 20
    IFs anidados, UNLESS y COND
    12 minutos
  21. 21
    Definición de funciones
    11 minutos
  22. 22
    Fundamentos de compilación de módulos
    6 minutos
  23. 23
    Guardas
    8 minutos
  24. 24
    Funciones anónimas
    7 minutos
  25. 25
    Capturar funciones
    4 minutos
  26. 26
    Invocación de funciones dentro del mismo módulo
    7 minutos
  27. 27
    Tuplas
    8 minutos
  28. 28
    Introducción al pattern matching
    8 minutos
  29. 29
    Pattern matching en funciones
    11 minutos
  30. 30
    Las tuplas :ok, :error
    7 minutos
  31. 31
    case
    10 minutos
  32. 32
    Operador pin
    7 minutos
  33. 33
    Pattern matchings y recursividad
    5 minutos
  34. 34
    Listas
    9 minutos
  35. 35
    Operadores y funciones de lista
    10 minutos
  36. 36
    Keyword lists: listas de palabras clave
    8 minutos
  37. 37
    Mapas
    7 minutos
  38. 38
    Pattern matching de mapas y keyword lists
    6 minutos
  39. 39
    Operadores y funciones para mapas y keyword lists
    5 minutos
  40. 40
    Estructuras con defstruct
    11 minutos
  41. 41
    Bitstrings
    11 minutos
  42. 42
    Charlists
    10 minutos
  43. 43
    Funciones de alto orden en Elixir
    5 minutos
  44. 44
    Uso de la función filter
    10 minutos
  45. 45
    Uso de la función map
    7 minutos
  46. 46
    Uso de la función reduce
    9 minutos
  47. 47
    Pipelines
    11 minutos
  48. 48
    Rangos y Streams
    11 minutos
  49. 49
    Funciones recursivas con listas
    14 minutos