Gotas de Elixir – Pattern Matching em argumentos

Na série de posts Gotas de Elixir, vamos apresentar algumas características que tornam Elixir uma linguagem funcional bastante divertida de se aprender. Para mais informações sobre a linguagem visite a página oficial: elixir-lang.org

Na maioria das linguagens, o operador “=” é usado para atribuir valores para variáveis, em Elixir ele é utilizado para Pattern Matching.

Em uma olhada rápida, atribuir valores a uma variável é bem similar a outras linguagens, mas por baixo dos panos outras coisas acontecem.

a = 1

Fazendo o match de uma variável com o número 1, ligamos o valor para a variável. Podemos também tentar fazer o match de um número para uma variável que já tenha um valor atribuído:

a = 1
1 = a

Atenção, variáveis só podem ser atribuídas se estiverem no lado esquerdo do operador! Caso contrário, uma exceção será lançada pois o compilador vai entender que a “variável” é na verdade uma função:

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

Caso não seja possível fazer o matching do lado direito com o lado esquerdo, uma exceção diferente será lançada:

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

Agora vem a parte realmente legal, ao chamar uma função em Elixir o compilador tenta fazer o matching entre os valores recebidos e os parâmetros declarados na função. Assim, podemos evitar fazer comparações dentro do código e tratar casos diferentes em funções separadas.

O exemplo clássico é o cálculo do somatório da sequência de Fibonnaci. A ideia aqui é, dado um número inteiro e positivo, calcular a soma de todos os números seguindo a sequência de Fibonacci.

Em ruby, uma implementação poderia ser, dado um número n, validar se o número passado como parâmetro é 0 ou 1, retornando 0 e 1 respectivamente. Caso contrário, vamos somar o resultado da sequência de fibonacci de  n-1 e n-2:

def fibonacci(n)
  return n if n == 0 || n == 1
  fibonacci(n-1) + fibonacci(n-2)
end

Em elixir, podemos tirar vantagem do Pattern Matching em argumentos de funções e separar os comportamentos em funções diferentes. Para tratar os casos onde n é 0 ou 1, criamos as seguintes funções:

defmodule Fibonacci do
  def calcular_soma(0) do 0 end
  def calcular_soma(1) do 1 end
end

Em elixir podemos ter múltiplas definições de funções, com mesmo nome mas com regras de comparação de argumentos diferentes. Ao chamar a função Fibonacci.calcular_soma os valores passados como argumento serão verificados com as possíveis definições, assim não precisamos utilizar os ifs como na definição em ruby.

O último passo é implementar a função para outros valores de n. Basta adicionar uma nova definição da função que tem uma variável como parâmetro e em seguida seguir a lógica de somar a sequência de n-1 e n-2:

defmodule Fibonacci do
  def calcular_soma(0) do 0 end
  def calcular_soma(1) do 1 end
  def calcular_soma(n) do
    calcular_soma(n-1) + calcular_soma(n-2)
  end
end

Semelhante a ruby, funções retornam o valor da última linha, mesmo que não seja utilizado o “return”.

Outro ponto importante é notar que a ordem de declaração das funções dentro do módulo faz diferença. As definições específicas devem vir primeiro, pois ao executar Fibonacci.calcular_soma(1) o compilador deve encontrar primeiro a definição que recebe 1 como argumento, ao invés da genérica que recebe uma variável com valor qualquer.

O que aconteceria caso a ordem de definição das funções fosse a seguinte?

defmodule Fibonacci do
  def calcular_soma(n) do
    calcular_soma(n-1) + calcular_soma(n-2)
  end
  def calcular_soma(0) do 0 end
  def calcular_soma(1) do 1 end
end

Todas as chamadas à Fibonacci.calcular_soma iriam entrar em um loop infinito, pois ao tentar fazer o Pattern Matching com a primeiro definição da função, qualquer valor será compatível.

E ai, gostou de conhecer um pouco sobre Elixir? Nos próximos posts vamos mostrar como lidar com conjuntos pode ser bem fácil e divertido com Elixir!

Anúncios

Um comentário sobre “Gotas de Elixir – Pattern Matching em argumentos

  1. […] O código acima é a solução comum para o problema. Se você não entendeu o motivo de termos duas funções normal, veja o post anterior sobre pattern matching. […]

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s