Blocos, Proc e Lambda
Estudo dos conceitos fundamentais de Blocos, Proc e Lambda em Ruby, abordando como criar, passar e executar blocos, utilizar yield, converter blocos em Proc, trabalhar com lambdas e entender as principais diferenças entre Proc e Lambda.
Blocos, Proc e Lambda
1. Blocos
Um bloco é um trecho de código que pode ser passado para um método para ser executado posteriormente. Blocos são muito usados em Ruby para repetição, iteração e personalização do comportamento de métodos. Existem duas formas de escrever um bloco:
Com {}
[1, 2, 3].each { |number| puts number }
Com do...end
[1, 2, 3].each do |number|
puts number
end
A convenção mais comum é usar {} para blocos pequenos e do...end para blocos com várias linhas.
Parâmetros do bloco
Os parâmetros ficam entre | |:
[1, 2, 3].each do |number|
puts number
end
Nesse exemplo, number recebe cada elemento da coleção.
yield
Dentro de um método, yield executa o bloco recebido:
def executar
puts "Antes"
yield
puts "Depois"
end
executar do
puts "Durante"
end
Resultado:
Antes
Durante
Depois
Um método pode executar o bloco várias vezes:
def repetir
3.times do
yield
end
end
repetir { puts "Olá" }
Verificando se existe um bloco
block_given? verifica se o método recebeu um bloco:
def executar
yield if block_given?
end
2. Proc
Um Proc é um objeto que representa um bloco de código. Isso permite armazenar um comportamento em uma variável e executá-lo posteriormente.
greet = Proc.new do |name|
"Olá, #{name}!"
end
greet.call("Tiago")
# "Olá, Tiago!"
Também é possível criar um Proc usando proc:
greet = proc do |name|
"Olá, #{name}!"
end
call
Para executar um Proc, podemos utilizar call:
double = proc { |number| number * 2 }
double.call(5)
# 10
Também existem outras formas de execução, como []:
double[5]
# 10
Passando um Proc para um método
Um Proc pode ser utilizado como comportamento reutilizável:
double = proc { |number| number * 2 }
[1, 2, 3].map(&double)
# [2, 4, 6]
O & transforma o Proc em um bloco para ser recebido pelo método.
3. Lambda
Uma Lambda também representa um bloco de código armazenado como objeto.
double = lambda do |number|
number * 2
end
double.call(5)
# 10
Também podemos utilizar a sintaxe curta:
double = ->(number) { number * 2 }
double.call(5)
# 10
Essa sintaxe é chamada de stabby lambda.
Lambda vs Proc
Para os conceitos básicos, as principais diferenças são:
| Lambda | Proc |
|---|---|
| Verifica a quantidade de argumentos com mais rigor | É mais flexível com argumentos |
return retorna da própria lambda | return pode retornar do método onde o Proc foi criado |
| É mais próxima de uma função | É mais próxima de um bloco Ruby |
| Exemplo de quantidade de argumentos: |
lambda { |a, b| a + b }.call(1)
# ArgumentError
Um Proc é mais permissivo:
proc { |a, b| [a, b] }.call(1)
# [1, nil]
Para código do dia a dia, lambdas são úteis quando queremos representar uma função, enquanto Proc é útil quando queremos armazenar ou reutilizar um comportamento baseado em blocos.
4. & e conversão entre bloco e Proc
O & permite converter entre um bloco e um Proc.
Bloco → Proc
def executar(&block)
block.call
end
executar do
puts "Olá"
end
Nesse caso, block é um Proc.
Isso é útil quando precisamos armazenar, passar ou executar o bloco explicitamente.
Proc → Bloco
double = proc { |number| number * 2 }
[1, 2, 3].map(&double)
# [2, 4, 6]
5. &:method
Ruby possui uma forma abreviada bastante comum para transformar um método em bloco:
["ruby", "rails", "ruby"].map(&:upcase)
# ["RUBY", "RAILS", "RUBY"]
É equivalente a:
["ruby", "rails", "ruby"].map { |word| word.upcase }
Outro exemplo:
[1, 2, 3].map(&:to_s)
# ["1", "2", "3"]
Essa sintaxe é especialmente comum com map, select, each e outros métodos que recebem blocos.