Pattern Matching com Elixir

Pattern Matching (Correspondência de Padrões ou Casamento de Padrões) é uma técnica muito utilizada por linguagens funcionais para desconstruir estruturas complexas. A princípio a técnica pode até parecer estranha, mas aos poucos você percebe a grande importância e logo não consegue mais viver sem ela. Vamos conhecê-la?

Para entender sobre Pattern Patching a primeira coisa que precisamos entender é o Match Operator, que comumente usamos em outras linguagens como operador de atribuição ou seja o igual (=). Veja esse exemplo:

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

Perceba que na primeira linha entendemos que n1 “recebe” 1, ou “atribui” 1. Só que na verdade, como o igual (=), no Elixir, não é o operador de atribuição e sim um operador de “match / correspondência / casamento”, o que ocorre é que o compilador “faz uma pergunta” mais ou menos assim…

A variável n1 pode ser correspondida com o valor 1?

Se isso for verdade, n1 apontará para 1 nas próximas instruções. Ou seja, isso quer dizer que será criado um vínculo entre a variável n1 e o número 1. E aqui já podemos notar uma diferença entre “n1 receber 1” e “n1 apontar para 1”, visto que “apontar” significa que a variável de fato é apenas um ponteiro que indica onde o valor apontado está. Veja essa figura:

Caso você não tenha entendido a figura acima, recomendo ler esse post onde falo sobre listas e tuplas, blz?

Agora vamos para a segunda linha do código (1 = n1)… O costume seria ler “1 recebe n1”, não é mesmo? Mas, por se tratar do operador match, lemos:

1 pode ser correspondido com N1?
iex(2)> 1 = n1
1

E nesse caso também é verdadeiro pois o n1 já aponta para 1 e vice-versa (conforme podemos observar na figura anterior).

Agora, na terceira linha (2 = n1) vemos algo diferente. A pergunta é…

2 pode ser correspondido com N1?
iex(3)> 2 = n1
** (MatchError) no match of right hand side value: 1

E observamos que um erro é levantado informando que …

“Não há correspondência com o lado da mão direita valor: 1”

Nesse momento há algo importante de se entender…

Match Operator

O Match Operator (=) é um operador binário, ou seja, ele precisa ter dois elementos para serem avaliados (um de cada lado). Sendo assim. Veja:

No Elixir, o Match Opertor, após a avaliação de correspondência, caso o resultado seja válido, sempre fará a atribuição na mão esquerda. Sendo assim, na linha 3 do nosso exemplo (2 = n1), o Elixir verifica que n1 aponta para o valor 1 e que 1 não corresponde a 2, e como a atribuição é feita sempre do lado esquerdo, por 2 não ser uma variável, não é possível uma reatribuição também.

Agora veja a continuação do exemplo…

iex(1)> n1 = 1
1
iex(2)> 1 = n1
1
iex(3)> 2 = n1
** (MatchError) no match of right hand side value: 1
iex(4)> n2 = n1
1
iex(5)> n2
1

Na linha 4, usamos o Match Operator…

“N2 pode ser correspondido com N1?”

E nesse caso, como n2 é uma variável, ela pode ser atribuída e passou a apontar para o mesmo local de n1, ou seja, o valor 1.

Pattern Matching

Agora, que já entendemos o Match Operator, vamos brincar com o Pattern Matching, que tem o mesmo princípio mas pode ser aplicado a estruturas mais complexas. Veja esse exemplo…

iex(1)> {a, b, c} = {:jackson, "pires", 123}
{:jackson, "pires", 123}
iex(2)> a
:jackson
iex(3)> b
"pires"
iex(4)> c
123

Perceba no exemplo acima, do lado esquerdo temos uma tupla constituídas apenas de variáveis, e do lado direito uma tupla com alguns valores.

Nesse momento, o Elixir verifica…

As estruturas podem ser correspondidas? — Sim pois as duas são tuplas. O primeiro elemento da tupla esquerda pode ser correspondido com o primeiro elemento da tupla direita? — Sim! O segundo elemento do lado esquerdo pode ser correspondido com o segundo elemento do lado direito? — Sim! E o terceiro? — Também!

Sendo assim, cada uma das variáveis, A, B e C serão apontadas cada um para seus respectivos valores isolados da tupla. E é isso que chamamos de “desconstruir” um tipo de dado complexo.

Observe abaixo que caso as estruturas não sejam equivalentes (em termos de quantidade de elementos ou de tipo de estrutura), o Pattern Matching não funciona.

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

Outra coisa interessante que podemos usar com Pattern Matching é a estrutura de cabeça e cauda para listas (como também já vimos aqui). Veja:

iex(1)> [cabeca | cauda] = [1, 2, 3]
[1, 2, 3]
iex(2)> cabeca
1
iex(3)> cauda
[2, 3]

Simples, não? :-)

Bom, por enquanto é isso, pessoal. Espero que tenham gostado e entendido a real necessidade de se usar Pattern Matching.

Vamos ficando por aqui, e como sempre, não se esqueçam de nos acompanhar nas redes sociais, curtir nossa página no Facebook e se inscrever em nossa newsletter semanal.

É isso! Até a próxima! ;-)

Referência: https://elixir-lang.org/getting-started/pattern-matching.html