Pattern Matching
Pattern matching em variáveis
Toda linguagem de programação tem a capacidade de criar variáveis. Você define um nome (talvez um tipo, dependendo da linguagem) e assina um valor a ela.
Elixir é um pouco diferente, mesmo parecendo igual a primeira vista, seu conceito muda.
Vamos a um exemplo simples onde criaremos uma map
chamado pessoa e dentro dele teremos o campo name
e genre
. Popularemos os valores do map
com My Name
e :no_binary
.
Vamos ao teste.
Rodando esse teste, obtemos o relatório de erro:
Vamos criar nosso módulo para passar o teste
Essa função apenas retorna um map com os valores que enviamos.
Se rodarmos novamente, os testes terão passados.
Analisando nosso teste, a definição de person
parece uma assinatura comum de variável, certo? chave = valor
. Mas por traz dos panos, elixir utiliza pattern matching para fazer isso. O valor a direita, apenas será adicionado a esquerda, caso de o match.
Em nosso exemplo, o requisito para matching é: se possuir algo ao lado direito, ele conseguira ser atribuído a variável person. Ele é bem aberto e pode receber qualquer tipo de dado.
Sei que parece confuso. Mas vamos continuar, logo tudo fará sentido.
Imagine agora que voce precisa pegar apenas name
e genre
que estão dentro do map retornado da função e coloca-los em uma variável cada. Para poder fazer as confiramações isoladas.
Algo assim:
Sem pattern matching, voce precisaria continuar com o valor person.name
ou atribuir ele a uma nova variável new_name = person.name
. O que por fim, daria na utilização do mesmo.
Com pattern matching é diferente, você pode obter diretamente o valor que quer, sem precisar de uma etapa intermediária.
O new_name
e new_genre
estão na posição onde está onde deveriam estar os valores certo? Ele é um espelho do que tem dentro da função, porem, no lugar dos valores, temos a definição de uma nova variável e é ai que o matching acontece. No nosso caso, estamos so dizendo que aceitamos qualquer valor que siga a estrutura de dentro do map, tendo name
sendo chamado agora de new_name
e a mesma coisa acontece com new_genre
.
Sendo assim, podemos utilizar diretamente a variável, porque após o matching, ela existe.
Se rodar o teste a cima, ele funcionará corretamente.
Outros exemplo:
IO.inspect
Função do módulo IO para espionar o que tem dentro de algum elemento. Mais sobre IO.inspect
Quando um matching não é sucedido ele dispara uma exceção.
Pattern matching em funções
Na função funciona do mesmo jeito, porém, utilizado nos parâmetros. Vamos a um exemplo. Precisamos de uma função que exiba um tipo de documento dependendo do tipo pedido. Vamos chamar essa função de render
e o módulo de Document
. Nossa função precisará de um ID para identificar o documento e um atom pedindo o tipo apropriado, nesse exemplo :txt
, :pdf
. Vamos cuidar do txt
primeiro:
Rodando esse teste obteremos um relatório comum de erro.
Nosso módulo ainda não existe. Vamos cria-lo:
Rodando o teste agora, tudo vai estar funcionando.
Vamos lidar agora com o PDF.
Se rodarmos novamente, teremos um relatório de erro.
Aqui as coisas começam a ficar interessantes. Estamos chamando a mesma função, mas com parâmetros diferente. Uma solução fácil para isso, é por um if dentro da função e retornar o valor que queremos. Algo assim:
Isso funcionária, mas se tornaria uma bagunça se precisarmos colocar mais tipos de arquivos. Com pattern matching, podemos controlar o fluxo das chamadas de funções, colocando nos parâmetros a formula do padrão para executar uma função.
Na definição da função, no segundo parâmetro, ao invés de colocar uma variável, setamos diretamente o valor. Por regra do Pattern matching, o valor deve ser igual para ele ser realizado com sucesso e ai executar a função. A primeira função render
so irá ser executada, quando o segundo parâmetro for :txt
e a segunda função apenas quando for :pdf
.
Se rodar o código, teremos um relatório positivo.
Caso nenhuma dessas condições seja cumprida, uma exceção é lançada. Para evitar isso, podemos criar uma função para avisar que o tipo não é suportado:
executando o teste, temos o seguinte relatório:
O próprio relatório nos da as opções válidas com :txt
e :pdf.
Vamos corrigir isso.
Na ultima função, troquei novamente o segundo parâmetro para variável, assim ele não espera um valor especifico e consegue executar a função. Tendo isso em mãos, criamos uma frase para avisar que o tipo informado não é válido.
Podemos rodar esse código e temos o relatório de sucesso:
Ordem de execução
A tentativa de execução de funções começa de cima para baixo. Quando uma função é atendida pelo pattern matching ela não vai para a próxima.
def render(_id, :txt), do: # ...
def render(_id, type), do: # Ela irá parar aqui
def render(_id, :pdf), do: # Nunca tentará fazer o pattern matching
Conclusão
Pattern matching é uma das principais funcionalidades do elixir. Versátil e fácil de usar, é uma excelente ferramenta para se aprender. Coisas complexas podem ser resolvidas de forma simples e elegante.
Last updated