🪄
Módulo #02

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:

LambdaProc
Verifica a quantidade de argumentos com mais rigorÉ mais flexível com argumentos
return retorna da própria lambdareturn 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.