Blog do TaQ

Dando um tapa no rubí

Publicado em Developer

Lupa

Vou comentar aqui um método novo encontrado no Ruby 1.9 (que mais tarde vou mostrar como é fácil de por no 1.8) chamado tap, que deu a inspiração para o título do post, influenciado pelo cômico Tapa na Pantera.

O que o tap faz é meio diferente do sentido da pantera quieta e sendo cutucada para despertar. Nesse caso, a pantera já está em movimento mas a gente quer saber exatamente o que está acontecendo nas entranhas dela (argh!). Falando um pouco mais seriamente, seria um método que ajuda em verificar o que acontece com os objetos criados durante o uso de encadeamento de métodos, o que ajuda tanto em um sentido de debug como de encurtar um pouco o código escrito. Vejamos.

Vamos supor que tenho esse array:

palavras = %w(o doce perguntou para o doce que doce que era mais doce o doce 
falou para o doce que era o doce de batata doce)
Quero saber agora o seguinte: a relação de palavras que tem comprimento com mais de 3 caracteres e que terminam com a letra A. Nada difícil:
p palavras.select {|item| item.size>3}.select{|item| item =~ /a$/i}.uniq.sort

Só que fiquei curioso para saber quais foram as palavras que tem mais de 3 caracteres retornadas ali no primeiro select. Da maneira, digamos, convencional, posso fazer o seguinte:

temp = palavras.select {|item| item.size>3}
p temp.uniq.sort
p temp.select {|item| item =~ /a$/i}.uniq.sort
=> ["batata", "doce", "falou", "mais", "para", "perguntou"]
["batata", "para"]

Funcionou direitinho, mas vejam que eu tive que usar uma variável temporária, temp, para verificar quais foram as palavras com mais de 3 caracteres. O método tap vai ser útil nesse ponto: inspecionar e interagir com o objeto corrente encontrado entre os métodos encadeados. Vamos ver esse mesmo exemplo usando tap:

p palavras.select {|item| item.size>3}.tap{|items| p items.uniq.sort}.
select{|item| item =~ /a$/i}.uniq.sort

Isso vai dar o mesmo resultado, com menos código se, nesse caso, quisermos resolver a nossa curiosidade. O tap nos forneceu ali um "espião" para o objeto corrente. Um ponto importante é que o valor retornado pelo bloco é o próprio objeto e não a última expressão avaliada no bloco, e o objeto não muda a não ser que se use um método destrutivo.

Vamos ver outro exemplo. Tenho o seguinte código:

a, b, c, d = 11, 1, 33, 2
puts (a-b)*(c+d)
=> 350
Digamos que o que rola ali é um cálculo mais complicado ou estejamos naqueles dias ruins de matemática mesmo, mas queremos saber porque o resultado deu 350. Podemos usar o tap para fazer isso:
puts (a-b).tap{|v| puts "esquerda: #{v}"} * (c+d).tap{|v| puts "direita : #{v}"}
esquerda: 10
direita : 35
350

Já dá uma ajudinha. E, para a turma que ainda não tem instalada alguma versão do Ruby 1.9, podemos alterar a classe Object (ê beleza hein!) e criar o tap nas versões anteriores também:

class Object
   def tap
      yield self
      self
   end
end


Tags:


Comentários

comments powered by Disqus

Twitter