Ruby1.8.7でインデックス付きmapを使う
mapをインデックス付きで使いたい!と思ってRuby1.8.7でRuby1.9ライクな書き方をしようとしたら失敗した。
Rubyでは内部イテレータが使われている。
[1,2,3].each { |n| print n }
イテレータにインデックスを渡すこともできる。
[1,2,3].each_with_index { |n,i| puts "#{i}: #{n}" }
ところでRuby 1.9からeachやmapはブロックを与えられなかった場合に外部イテレータを返すようになった。これにより、こんな書き方もできるようになった。
e = [1,2,3].each e.with_index { |n,i| puts "#{i}: #{n}" }
これで何が嬉しいって、mapでも簡単にインデックス番号を使えるようになったのである。*1
[1,2,3].map.with_index { |n,i| [i,n*2] } #=> [[0,2],[1,4],[2,6]]
そして、Ruby1.8.7はRuby1.9からのバックポートを多く含み、その中には外部イテレータもあるから、もちろん同じことができる…と思ったのだが。
NoMethodError: undefined method `with_index' for [1, 2, 3]:Array
あるえぇぇ?
実はRuby1.8.7のmapは互換性を維持するために旧来どおりに動作する、つまり外部イテレータを返さない。
じゃあどうするかというと、eachは外部イテレータを返すので、それを前に出せばいいわけですな。
[1,2,3].each_with_index.map { |n,i| [i,n*2] }
…そんな話が、今から約二年前にruby-devで解決されてました!
orz
*1:私はcollectよりもmap派。