Dando um tapa no rubí
Publicado em Developer

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) => 350Digamos 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
Comentários fechados.
Muito bom, substitui o feio obj.instance_eval{|obj| ...; obj}...