end
# Bye bye, 3
# Bye bye, 2
# Bye bye, 1
# => nil
O until (até que em inglês) faz justamente o contrário. Ele executa um bloco
associado de código até que a situação se faça verdadeira:
a = [1, 2, 3]
until a.empty? do
puts "Bye bye, #{a.pop}"
end
# Bye bye, 3
# Bye bye, 2
# Bye bye, 1
# => nil
Cuidados com until
O until possui o mesmo problema semântico do unless (seção 2.4),
ou seja, podemos ter problemas ao pensar em dupla negativa. Dessa
forma, recomenda-se usar apenas while, negando a expressão quando
pertinente.
for ... in
O for ...
in é uma maneira de iterar elementos de uma coleção, ou um objeto
iterável, ou seja, um objeto que seja um Enumerator. A estrutura do for é simples:
34
Casa do Código
Capítulo 2. Conhecendo Ruby
for variável in coleção
Vejamos um exemplo com Arrays, que são iteráveis":
fruits = %w{pera uva maçã}
for fruit in fruits
puts "Gosto de " + fruit
end
# Gosto de pera
# Gosto de uva
# Gosto de maçã
# => ["pera", "uva", "maçã"]
Hashes também são iteráveis, com uma pequena diferença:
frequencies = {'hello' => 10, 'world' => 20}
for word, frequency in frequencies
puts "A frequência da palavra '#{word}' é #{frequency}"
end
# A frequência da palavra 'hello' é 10
# A frequência da palavra 'world' é 20
# => {"hello"=>10, "world"=>20}
Blocos
Blocos são estruturas singulares a Ruby, e são extremamente importantes, tanto é
que merecem atenção especial. Blocos são trechos de código associados a um método
que podem ser executados a qualquer momento por este método.
Este conceito é um pouco estranho para quem vem de linguagens como Java
(apesar de que Java possui uma forma de se fazer blocos, com classes anônimas) e C,
mas se você já ouviu falar de lambdas, está familiarizado com o conceito.
O exemplo mais clássico de blocos é o uso de iteradores:
fruits = %w{pera uva maçã}
fruits.each do |fruit|
puts "Gosto de " + fruit
end
# Gosto de pera
# Gosto de uva
35
2.4. Fluxos e laços
Casa do Código
# Gosto de maçã
# => ["pera", "uva", "maçã"]
Explicando o exemplo anterior, o método #each pega cada valor dentro de sua
coleção e repassa para o bloco associado, através da variável fruit.
Outro método que usa bloco bastante útil é o #map, de Hashes e Arrays, que
retorna um novo Array contendo como elementos o resultado devolvido a cada ite-
ração:
numbers = [1, 2, 3, 4]
squared_numbers = numbers.map { |number| number * number }
squared_numbers # [1, 4, 9, 16]
Notação para blocos
Como você pôde perceber, foram usadas duas notações distintas nos
exemplos anteriores, a notação com do ...
end e a notação com cha-
ves. Ambas funcionam da mesma maneira. Porém, a comunidade Ruby
adotou a seguinte regra:
Para blocos curtos, de apenas uma linha, adota-se as chaves;
Para blocos longos, de duas ou mais linhas, adota-se o do ...
end.
A grande utilidade de blocos é que existem inúmeras APIs que exigem operações
antes e depois de serem utilizadas. Com o uso de blocos, este procedimento fica
transparente, como é possível observar no próximo exemplo:
# Exemplo tradicional
file = File.new('file.txt', 'w')
file.puts "Escrevendo no arquivo"
file.close
# Ao invés de termos que nos preocupar com a abertura e o fechamento do
# arquivo, a própria API faz isso antes e depois do bloco.
36
Casa do Código
Capítulo 2. Conhecendo Ruby
File.open('another_file.txt', 'w') do |file|
file.puts "Escrevendo no arquivo com blocos!"
end
É possível também passar e receber mais de um argumento para blocos. O fun-
cionamento é bem o mesmo de argumentos para funções, vejamos o exemplo abaixo
com Hashes:
a = {:a => 1, :b => 2, :c => 3}
a.each do |key, value|
puts "A chave #{key} possui valor #{value}"
end
# A chave a possui valor 1
# A chave b possui valor 2
# A chave
c possui valor 3
Escopo de variáveis
Variáveis declaradas no mesmo escopo em que o bloco se encontra são acessíveis
de dentro deste bloco.
a = %w{a b c d e}
counter = 0
a.each { |val| counter += 1 }
puts "O valor do contador é: #{counter}" # O valor do contador é: 5
Se usarmos um nome de variável para o parâmetro de bloco que é o mesmo que
uma variável externa ao bloco, a variável externa deixará de ser acessível dentro do
bloco, mas terá seu valor mantido:
a = %w{a b c}
i = 5
a.each do |i|
puts i
end # a; b; c
puts i # 5
37
2.4. Fluxos e laços
Casa do Código
Atenção ao escopo!
O comportamento do exemplo anterior é restrito à variáveis em parâ-
metros. Isso significa que variáveis externas ao bloco são modificáveis
dentro deste mesmo bloco, conforme o primeiro exemplo. O resultado
disso pode ser inesperado, já que o bloco pode estar alterando valores de
outras variáveis.
As variáveis declaradas dentro do bloco são criadas a cada vez que o bloco é
chamado, ou seja, essas variáveis não existirão caso o bloco seja chamado novamente
e serão criadas novamente:
a = %w{w o r d}
a.each do |letter|
word ||= ""
word << letter
puts word
end
# w
# o
# r
# d
Existem situações em que nós não ligamos para um dos valores passados para