Enumerable#grep を使うときに Regexp.last_match をさわりたい

うまいタイトルが思い付かない。

Ruby は $~ や Regexp.last_match などで、カレントスコープで実行した最後の正規表現マッチの結果を得ることができる。

/(.)(.)/ === 'abcdef'
puts $1 # => a
puts $2 # => b

こんな風に。

ところで、 Ruby には Enumerable#grep というメソッドがある。コレクションのそれぞれの要素と引数を === 演算子で比較して、 true だった要素を集めた配列を返すメソッド。 === という演算子で比較するので、正規表現に限らず、たいていのオブジェクトで比較することができる。

ただし、 Enumerable#grep はイテレータを使って実装されており、イテレータではブロックローカルなスコープが導入される。つまり、カレントスコープで実行されないので、 Regexp.last_match などでマッチの結果を得ることができない。

では、どうするかというと、ブロックつきで Enumerable#grep を呼べばいい。ブロックつきで呼ぶと、ブロックを評価した結果の配列になる。

%w(abc def ghi).grep(/^a/) {|i| i.capitalize } # => ['ABC']

こんなかんじで。 Enumerable#map を同時に実行するようなもの、と考えていいとおもう。

で、ブロックではブロックローカルなスコープになるわけで、ここで $~ などが望んだ通りの値を返してくれる。

%w(abc def ghi).grep(/(.).(.)/) { [$2, $1] } # => [['c','a'],['f','d'],['i','g']]

このとおり。