tapかわいいよtap

Ruby 1.9で導入されたObject#tapはRuby 1.8でも1.8.7から使えるようになった。
tapメソッドとは何かというと、自分自身にブロックを作用させて自分自身を返すだけのメソッドだ。つまり副作用専門メソッド。定義はいたって簡単、コレ。

class Object
  def tap
    yield(self)
    self
  end
end

たとえば、1〜4の二乗のうち10以上のものを求めたいんだけど、デバッグ時とかで途中経過である1〜4の二乗を表示してほしいなんて場合はこんな感じに。いらなくなったらtapの部分を消せばいい。楽チン。

ary = [1, 2, 3, 4]
ary.map{|x| x**2}.tap{|a| p a}.select {|x| x>10 } # => [16]
# >> [1, 4, 9, 16]

ほかにも、破壊的メソッドを適用した後で自分自身を返す場合にも有用だ。Hashに値を順次設定していく場合なんだが、Hash#[]=は設定した値を返す。だけどinjectを使うためにはHashをブロックの評価値にしないといけない。この場合は「; h」と書けばすむのだが、tapを使ったほうがナウな感じになる。

ary = [3, 7, 6, 12]
ary.inject({}) {|h, x| h[x] = x**2; h }       # => {3=>9, 7=>49, 6=>36, 12=>144}
ary.inject({}) {|h, x| h.tap{ h[x] = x**2 } } # => {3=>9, 7=>49, 6=>36, 12=>144}

あと、初期値が配列やハッシュの場合はおもむろにtapで更新させちゃうのもよい。こう書くことでローカル変数「h」が局所化されるから読みやすくなる。こんな感じにローカル変数の局所化にもtapは使えるぜ。しかもこの方法だとinjectよりもちょっとばかし速い。おまけに短いし、injectが苦手な人にも理解しやすい。

{}.tap {|h| ary.each{|x| h[x] = x**2} }       # => {3=>9, 7=>49, 6=>36, 12=>144}

tapは応用範囲が広いし、メソッドチェーンで美しく書くことができる。まさしくコロンブスの卵だ。

tapかわいいよtap。

追記

どうも、xmpfilterの再実行を忘れてしまって結果が反映されてませんでした。指摘感謝です。

[1,2,3,4].map{|x| x**2}.select{|x| (x>8 && x<10).tap{|b| p [b, x]}} # => [9]
# >> [false, 1]
# >> [false, 4]
# >> [true, 9]
# >> [false, 16]

なるほど、selectの条件式にtapを適用するのもありですね。これは盲点だった。