plos a seguir.
Primeiramente, vejamos como que funciona o #to_proc:
upcase_it = :upcase.to_proc
upcase_it.call('abcde') # ABCDE
upcase_it.call(123) # NoMethodError: undefined method
# ùpcase' for 123:Fixnum
Ou seja, o proc gerado basicamente chama o método no objeto. Porém, usado
com o &, podemos criar filtros de maneira simples e enxuta:
# Forma tradicional usando bloco
%w{pera uva jaca}.map { |fruit| fruit.upcase }
# ["PERA", "UVA", "JACA"]
# Usando o #to_proc
%w{pera uva jaca}.map(&:upcase)
# ["PERA", "UVA", "JACA"]
47
2.5. Funções, blocos, lambdas e closure
Casa do Código
lambdas
Lambdas são bem parecidos com procs, e muitas vezes são usados de forma in-
tercambiável. A forma de construir lambdas é bastante parecida com procs:
upcase_it = lambda { |x| x.upcase }
upcase_it.call("abc") # ABC
# Arity é o número de parâmetros que o lambda aceita
upcase_it.arity # 1
Porém, lambdas possuem um atalho especial:
upcase_it = ->(x) { x.upcase }
upcase_it.call("abc") # ABC
Não existe uma recomendação para o uso das notações. Porém a primeira é mais
utilizada apenas por razões históricas: a notação ->() é uma introdução recente à
linguagem. Fique à vontade para usar a que você mais gostar.
Diferenças entre procs e lambdas
Apesar de serem parecidos, procs e lambdas não são iguais em dois comporta-
mentos.
Primeiro, vimos anteriormente que variáveis em excesso não causam nenhum
problema com blocos e são simplesmente ignorados, e isso acontece com procs, mas
não com lambdas:
show = proc { |x, y| puts "#{x}, #{y}" }
show.call(1)
# 1,
show.call(1, 2, 3)
# 1, 2
show = ->(x,y) { puts "#{x}, #{y}" }
show.call(1, 2)
# 1, 2
show.call(1)
# ArgumentError: wrong number of arguments (1 for 2)
show.call(1, 2, 3)
# ArgumentError: wrong number of arguments (3 for 2)
A segunda diferença é no uso do return. Dentro de blocos, quando usamos
return, o que de fato é retornado é o método associado, já que blocos/procs não
consideram o return. Em contrapartida, lambdas são mais próximos a métodos e
funções e retornam apenas ao contexto a que pertencem:
48
Casa do Código
Capítulo 2. Conhecendo Ruby
def proc_stop
puts "Cheguei..."
proc { puts "Hey"; return; puts "Ho!" }.call
puts "Saindo..."
end
proc_stop # Cheguei...; Hey
def lambda_stop
puts "Cheguei..."
lambda { puts "Hey"; return; puts "Ho!" }.call
puts "Saindo..."
end
lambda_stop # Cheguei...; Hey; Saindo...
closures
Closure, ou fechamento, é uma característica de funções que podem ser as-
sociadas a variáveis e podem ser invocadas a qualquer momento. Quando funções
com essa característica são criadas, elas carregam em si não apenas o código a ser
executado mas também referências à todas as variáveis existentes naquele escopo.
Em Ruby, lambdas e procs são closures, portanto carregam em si referências às
variáveis em seu escopo. Vejamos um exemplo:
def create_lambda
value = 10
-> { value += 1; puts value }
end
l = create_lambda
l.call # 11
l.call # 12
O que acontece no exemplo anterior é que, quando o lambda é criado, a va-
riável value está em seu escopo e portanto o lambda possui uma referência à value.
Mesmo quando a função create_lambda termina de executar, o lambda l ainda con-
segue acessar a variável value pois ainda possui uma referência. Note o exemplo a
seguir, criando duas lambdas:
49
2.5. Funções, blocos, lambdas e closure
Casa do Código
def create_lambda
value = 10
-> { value += 1; puts value }
end
first_lambda = create_lambda
next_lambda = create_lambda
first_lambda.call # 11
next_lambda.call # 11 - "value" é outra variável neste escopo
first_lambda.call # 12
first_lambda.call # 13
Quando executamos create_lambda pela segunda vez, uma nova variável value
foi criada e associada ao escopo de next_lambda.
Dessa forma, next_lambda e
first_lambda referenciam-se à diferentes value, criadas em momentos diferentes.
Agora vamos modificar um pouco o código. Vamos criar duas lambdas dentro
de um mesmo escopo:
def create_lambdas
value = 10
first = -> { value += 1; puts value }
last = -> { value += 1; puts value }
[first, last]
end
first_lambda, last_lambda = create_lambdas
first_lambda.call # 11
last_lambda.call # 12 - "value" é a mesma variável
first_lambda.call # 13
last_lambda.call # 14
É possível observar no exemplo anterior que ambos first_lambda e
last_lambda carregam o mesmo escopo, ou seja, compartilham a mesma re-
ferência à variável value.
50
Casa do Código
Capítulo 2. Conhecendo Ruby
Dica: cuidado com escopo de closures
Há situações que não é possível saber que momento e nem em que or-
dem que lambdas e procs são executados pois são propagados à outras
partes do código. Dessa forma, compartilhar variáveis via closures pode
resultar em comportamento difícil de prever e bugs bem complicados de
serem encontrados. Por isso, preste bastante atenção nas variáveis sendo
compartilhadas via closures.
2.6
Classes e módulos
Ruby é uma linguagem orientada a objetos, e portanto, não poderia faltar uma sin-