Blocos em Ruby

Os blocos de código Ruby (chamados de closures ou fechamentos em outras linguagens) são definitivamente uma das funcionalidades mais legais do Ruby. São pedaços de código entre chaves ou entre do end que você pode associar com a invocação de métodos, quase como se fossem parâmetros. Um bloco Ruby é um modo de agrupar declarações, e pode aparecer apenas no código adjacente a uma chamada de método; o bloco é escrito iniciando-se na mesma linha do último parâmetro da chamada do método (ou o parênteses da lista de parâmetros). O código no bloco não é executado quando é encontrado. Ao invés disso, o Ruby se lembra do contexto em que o bloco aparece (as variáveis locais, o objeto atual e assim por diante) e então entra no método.

O padrão do Ruby é o de usar chaves para blocos de uma única linha e do end para blocos que ocupem múltiplas linhas. Lembre-se que a sintaxe de chaves possui maior precedência que a sintaxe do end.

O Matz diz que qualquer método pode ser chamado com um bloco como argumento implícito. Dentro do método, você pode chamar o bloco usando a palavra-chave yield (ceder) com um valor.

Além disso, como você irá aprender em breve, blocos podem ter seus próprios argumentos. Existem muitos métodos em Ruby que iteram sobre um intervalo de valores. A maioria destes iteradores são escritos de modo que seja possível receber um bloco de código como parte de sua sintaxe de invocação. O método pode então passar o controle para o bloco de código (isto é, executar o bloco) durante a execução quantas vezes for necessário para a iteração se completar (por exemplo, se estivermos iterando por valores de um array, podemos executar o bloco para cada elemento do array).

Uma vez que você criou um bloco, pode associá-lo com uma chamada a um método. Geralmente os blocos de código passados para os métodos são objetos anônimos, criados no momento. Por exemplo, no código seguinte, o bloco contendo puts “Olá” é associado com a chamada ao método saudacao.

1   saudacao {puts 'Olá'}

Se o método tem parâmetros, eles aparecem antes do bloco:

1   saudacao_verbosa("PuneRuby") {puts 'Olá'}

Um método pode então invocar um bloco associado uma ou mais vezes usando a instrução Ruby yield. Deste modo qualquer método que queira ter um bloco como parâmetro pode usar a palavra-chave yield para executar o bloco a qualquer hora.

O programa p022codeblock.rb ilustra o que acabamos de discutir.

 1 =begin
 2   Os blocos de código Ruby são pedaços de código entre chaves ou entre
 3   do end que você pode associar com invocações (chamadas) de método
 4 =end
 5 
 6 def chama_bloco
 7   puts 'Inicio do método'
 8   # você pode chamar o método com a palavra-chave yield
 9   yield
10   yield
11   puts 'Fim do metodo'
12 end
13 # Os blocos de código podem aparecer apenas no código adjacente a uma chamada de método
14 chama_bloco {puts 'Dentro do bloco'}

A saída desse programa é:

1 ruby p022codeblock.rb
2 Início do metodo
3 Dentro do bloco
4 Dentro do bloco
5 Fim do metodo
6 >Exit code: 0

Se você fornecer um bloco de código quando chamar um método, então dentro desse método, você pode ceder (yield) o controle para esse bloco de código – suspende a execução do método; executa o código no bloco; e retorna o controle para o corpo do método, logo após a chamada a yield. Se nenhum bloco de código foi passado, o Ruby lança uma exceção avisando que nenhum bloco foi dado:

1 no block given (LocalJumpError)

Você pode prover parâmetros para a chamada a yield: estes serão passados para o bloco. Dentro do bloco, você lista os nomes dos argumentos para receber os parâmetros entre barras verticas (|).

O programa p023codeblock2.rb ilustra isso.

1 # Você pode prover parâmetros a chamada a yield:
2 # estes serão passados ao bloco
3 def chama_bloco
4   yield('ola', 99)
5 end
6 chama_bloco {|str, num| puts str + ' ' + num.to_s}

A saída é:

1 >ruby p023codeblock2.rb
2 ola 99
3 >Exit code: 0

Perceba que o código no bloco não é executado até que ele é encontrado pelo interpretador Ruby. Ao invés disso, o Ruby se lembra
do contexto em que o bloco aparece e então entra no método.

O valor de returno de um bloco de código (assim como o de um método) é o valor da última expressão avaliada no bloco. Este valor de retorno é disponibilizado dentro do método; ele vem como o valor de retorno do yield.

block_given? (bloco dado) retornaria verdadeiro caso o yield executasse um bloco no contexto atual. Veja o seguinte exemplo:

 1 def tentar
 2   if block_given?
 3     yield
 4   else
 5     puts "nenhum bloco"
 6   end
 7 end
 8 tentar # => "nenhum bloco"
 9 tentar { puts "ola" } # => "ola"
10 tentar do puts "ola" end # => "ola"

Variáveis de bloco

Vamos ver o que acontece no exemplo seguinte em que uma variável externa a um bloco é x um um parâmetro do bloco também é x.

1 x = 10
2 5.times do |x|
3   puts "x dentro do bloco: #{x}"
4 end
5 
6 puts "x fora do bloco: #{x}"

A saída é:

1 x dentro do bloco: 0
2 x dentro do bloco: 1
3 x dentro do bloco: 2
4 x dentro do bloco: 3
5 x dentro do bloco: 4
6 x fora do bloco: 10

Você observará que após a execução do bloco, o x fora do bloco é o x original (10). Portanto, o parâmetro x do bloco é local ao mesmo.

Em seguida, observe o que acontece com x no seguinte exemplo:

1 x = 10
2 5.times do |y|
3   x = y
4   puts "x dentro do bloco: #{x}"
5 end
6 
7 puts "x fora do bloco: #{x}"

A saída é:

1 x dentro do bloco: 0
2 x dentro do bloco: 1
3 x dentro do bloco: 2
4 x dentro do bloco: 3
5 x dentro do bloco: 4
6 x fora do bloco: 4

Como neste caso x não é um parâmetro do bloco, a variável x é o mesmo dentro e fora do bloco.

Resumo

Listei todos os pontos importantes que você precisa se lembrar após ter completado os tópicos da parte 3 desse tutorial.

Logo do Guru-SP

Este material tem como base o tutorial do RubyLearning.com de Satish Talim e foi traduzido por membros do GURU-SP com a permissão do autor.

Ajude o RubyLearning participando em algum dos cursos pagos ou fazendo uma doação para o projeto

Voltar para o índice