Skip to content

Latest commit

 

History

History
206 lines (154 loc) · 6.34 KB

pattern-matching.markdown

File metadata and controls

206 lines (154 loc) · 6.34 KB
section layout title
getting-started
getting-started
Pattern matching

Nota del traductor: el término pattern matching en inglés no tiene una traducción sensible en castellano, o de uso común en el ambiente informático. Debido a esto se decidió dejar la frase en inglés y además usar la palabra del inglés match, para indicar cuando se produce la coincidencia de patrones.

En este capítulo mostraremos como el operador = en Elixir es, de hecho, un operador de match o emparejamiento de patrones, y como usar pattern match dentro de estructuras de datos. Finalmente, aprenderemos sobre el operador de fijado ^, usado para acceder valores ligados previamente a una variable.

El operador de match

Hemos usado el operador = un par de veces antes para asignar variables en Elixir:

iex> x = 1
1
iex> x
1

En Elixir, el operador = se denomina el operador de match. Veamos por qué:

iex> x = 1
1
iex> 1 = x
1
iex> 2 = x
** (MatchError) no match of right hand side value: 1

Nota que 1 = x es una expresión válida, y que no produjo error porque tanto el lado izquierdo como el derecho del operador son iguales a 1. Cuando los operandos no coinciden, se dispara una excepción MatchError, como se muestra en la tercer línea.

Una variable sólo puede ser asignada si se encuentra en el lado izquierdo del operador =:

iex> 1 = unknown
** (CompileError) iex:1: undefined function unknown/0

Dado que no se definió previamente la variable unknown, Elixir asume que estabas tratando de llamar a la función unknown/0, pero dicha función no existe.

Pattern matching

El operador de match no sólo se usa para emparejar contra valores simples, sino que también es útil para desestructurar tipos de datos más complejos. Por ejemplo, podemos hacer pattern matching en tuplas:

iex> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}
iex> a
:hello
iex> b
"world"

Un error de match ocurrirá si los lados no pueden ser emparejados, por ejemplo, si las tuplas tienen tamaños diferentes:

iex> {a, b, c} = {:hello, "world"}
** (MatchError) no match of right hand side value: {:hello, "world"}

También si se comparan tipos de datos diferentes, por ejemplo, si se intenta emparejar una tupla en el lado izquierdo con una lista en el derecho:

iex> {a, b, c} = [:hello, "world", 42]
** (MatchError) no match of right hand side value: [:hello, "world", 42]

Curiosamente, podemos hacer match en valores específicos. El ejemplo de código siguiente se asegura que el lado izquierdo hará match sólo cuando el lado derecho sea una tupla que comience con el átomo :ok:

iex> {:ok, result} = {:ok, 13}
{:ok, 13}
iex> result
13

iex> {:ok, result} = {:error, :oops}
** (MatchError) no match of right hand side value: {:error, :oops}

Podemos hacer pattern matching en listas:

iex> [a, b, c] = [1, 2, 3]
[1, 2, 3]
iex> a
1

Una lista además soporta hacer matching en su cabeza y su cola:

iex> [cabeza | cola] = [1, 2, 3]
[1, 2, 3]
iex> cabeza
1
iex> cola
[2, 3]

De manera similar a las funciones hd/1 y tl/1, no podemos hacer match con el patrón [cabeza | cola] en listas vacías:

iex> [cabeza | cola] = []
** (MatchError) no match of right hand side value: []

El formato [cabeza | cola] no sólo se usa en pattern matching, sino que también es útil para agregar elementos al comienzo de una lista:

iex> lista = [1, 2, 3]
[1, 2, 3]
iex> [0 | lista]
[0, 1, 2, 3]

La técnica de pattern matching permite a los desarrolladores desestructurar fácilmente tipos de datos complejos como tuplas y listas. Como veremos en los próximos capítulos, es uno de los fundamentos usados para realizar recursión en Elixir, y aplica a otros tipos de datos, como mapas (maps) y binarios (binaries).

El operador de fijado

En Elixir, las variables pueden ser ligadas a nuevos valores:

iex> x = 1
1
iex> x = 2
2

Sin embargo, hay ocasiones en que no queremos que esto ocurra.

Usa el operador de fijado ^ cuando desees hacer pattern matching contra el valor ligado a la variable anteriormente, en vez de ligar un nuevo valor.

iex> x = 1
1
iex> ^x = 2
** (MatchError) no match of right hand side value: 2

Dado que hemos fijado el valor de x a 1, la segunda línea es equivalente a lo siguiente:

iex> 1 = 2
** (MatchError) no match of right hand side value: 2

Nota que vemos el mismo mensaje de error.

Podemos usar el operador de fijado dentro de desestructuraciones, en tuplas y listas:

iex> x = 1
1
iex> [^x, 2, 3] = [1, 2, 3]
[1, 2, 3]
iex> {y, ^x} = {2, 1}
{2, 1}
iex> y
2
iex> {y, ^x} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}

Debido a que x fue ligado al valor 1 al fijarlo, el último ejemplo se podría haber escrito de la siguiente manera:

iex> {y, 1} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}

Si una variable se usa más de una vez en un patrón, todas las referencias estarán ligadas al mismo valor:

iex> {x, x} = {1, 1}
{1, 1}
iex> {x, x} = {1, 2}
** (MatchError) no match of right hand side value: {1, 2}

En algunas ocasiones no te importará un valor particular en un patrón. Es práctica común ligar esos valores que no te interesan a la variable _ o a variables que comiencen con este caracter. Por ejemplo, si sólo nos interesa la cabeza de una lista, podemos asignar la cola a _:

iex> [cabeza | _] = [1, 2, 3]
[1, 2, 3]
iex> cabeza
1

La variable _ es especial: no se puede leer su contenido. Si se intenta leer su contenido Elixir lanza una excepción de error de compilación (CompileError):

iex> _
** (CompileError) iex:1: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions

Aunque el pattern matching nos permite realizar construcciones poderosas, su uso es limitado. Por ejemplo, no se pueden hacer llamadas a funciones en el lado izquierdo del match. El siguiente ejemplo es inválido:

iex> length([1, [2], 3]) = 3
** (CompileError) iex:1: cannot invoke remote function :erlang.length/1 inside match

Esto concluye la introducción a pattern matching. Como veremos en el próximo capítulo, pattern matching es muy común en muchas construcciones del lenguaje.