Pattern matching en funciones

El pattern matching de Elixir puede ser aprovechado en funciones para distintos propósitos. Por ejemplo, en vez de usar guardas, si queremos forzar a meter parámetros concretos en posiciones concretas, podemos poner una constante en los parámetros. Además, las funciones de destructuring también están disponibles.

El pattern matching es una herramienta poderosa que podemos aprovechar especialmente en las funciones para escribir código más limpio y eficiente. En lugar de depender únicamente de guardas o condicionales, podemos usar patrones específicos en los parámetros para que la función que se ejecute sea la que mejor encaje con los valores que recibimos.

Cuando invocamos una función, el lenguaje busca entre todas las definiciones de esa función la que coincida en nombre y número de parámetros. Además, si hay guardas, estas deben cumplirse para que la función sea seleccionada. Pero el pattern matching va más allá: podemos hacer que los parámetros mismos sean patrones que deben coincidir para que la función se ejecute.

Por ejemplo, podemos definir varias funciones con el mismo nombre pero con patrones distintos en sus parámetros. Si uno de los parámetros es una constante, como el átomo :OK, la función solo se ejecutará si ese parámetro coincide exactamente con esa constante. Esto nos permite manejar casos especiales de forma muy clara y directa, sin necesidad de escribir condicionales dentro de la función.

Veamos un ejemplo sencillo para ilustrar esto:

defmodule Ejemplo do
  def prueba(atomo, cadena) do
    IO.puts(cadena)
  end

  def prueba(:OK, _cadena) do
    IO.puts("Me has pasado el átomo OK")
  end
end

Ejemplo.prueba(:OK, "hola")    # Imprime: Me has pasado el átomo OK
Ejemplo.prueba(:NO, "hola")    # Imprime: hola

En este caso, cuando llamamos a prueba con el átomo :OK como primer parámetro, se ejecuta la segunda definición, que detecta ese caso especial. Si pasamos cualquier otro átomo, se ejecuta la primera función.

Además, podemos combinar el pattern matching con guardas para controlar aún más cuándo se ejecuta cada función. Por ejemplo, en una función de división, podemos definir un caso especial para cuando el divisor sea cero y devolver un valor especial como :infinito:

defmodule Calculadora do
  def dividir(_a, 0), do: :infinito
  def dividir(a, b) when is_number(a) and is_number(b), do: a / b
end

Calculadora.dividir(5, 0)   # Devuelve :infinito
Calculadora.dividir(5, 2)   # Devuelve 2.5

Si intentamos pasar parámetros que no sean números, como cadenas, la función no matcheará y se producirá un error, a menos que definamos un caso para manejar esos errores.

Otra ventaja del pattern matching en funciones es el destructuring, que nos permite extraer directamente valores de estructuras como tuplas en los parámetros. Por ejemplo, si recibimos una tupla con tres elementos, podemos extraerlos directamente en la definición de la función:

defmodule Ejemplo do
  def print2({_ok, mensaje}) do
    IO.puts(mensaje)
  end
end

Ejemplo.print2({:ok, "Hola"})   # Imprime: Hola

Esto es especialmente útil cuando trabajamos con funciones que devuelven tuplas para indicar éxito o error, como muchas funciones relacionadas con archivos. Podemos definir funciones que manejen directamente el caso de éxito y el caso de error usando patrones distintos:

defmodule Ejemplo do
  def print2({:ok, mensaje}) do
    IO.puts("Todo fue bien: #{mensaje}")
  end

  def print2({:error, _razon}) do
    IO.puts("Algo ha salido mal")
  end
end

Ejemplo.print2({:ok, "Buenos días"})    # Imprime: Todo fue bien: Buenos días
Ejemplo.print2({:error, :not_found})    # Imprime: Algo ha salido mal

Si pasamos un valor que no coincide con ninguno de los patrones, se producirá un error porque no hay una función que lo maneje.

En definitiva, el pattern matching en funciones nos permite escribir código más expresivo y claro, manejando casos especiales y extrayendo datos de estructuras complejas directamente en los parámetros, lo que reduce la necesidad de condicionales y hace que nuestro código sea más fácil de entender y mantener.

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