with_index

Rubyの外部イテレータ使えない…。ちなみに以下のサンプルは1.9.0でテストした。

> (1..4).each.with_index.to_a
=> [[1, 0], [2, 1], [3, 2], [4, 3]]
> e = (1..4).each.with_index
=> #<Enumerable::Enumerator:0xc8c404>
> e.next
1

何でe.nextで[1, 0]でなくて1が返るのかな?with_indexの意味がない。もちろん内部イテレータだったら以下のように問題ない。

> (1..4).each.with_index {|v, i|
*   puts("#{v}, #{i}")
> }
1, 0
2, 1
3, 2
4, 3
=> 1..4

そこで、Pythonのenumerate()関数相当のものを作成してみた。

> def enumerate(seq)
>   i = 0
>   seq.each {|v|
*     yield [i, v]
>     i += 1
>   }
> end
=> nil
> enumerate(1..4) {|i, v|
*   puts("#{i}, #{v}")
> }
0, 1
1, 2
2, 3
3, 4
=> 1..4

Rubyでyield使うと内部イテレータになってしまう。forと組み合わせるにはPythonのような外部イテレータを返すようにenumerate()を作成したい。調べてみたらRubyで外部ジェネレータを作成するにはgeneratorモジュールを使うみたい。リファレンスマニュアルを見ながらサンプルを試したら、1.9.0ではエラーが発生した。リファレンスマニュアルのサンプルが更新されていない。どうすれば良いのだろう?


とりあえず、1.8.6ではできたので貼っておく。

> requre 'generator'
=> true
> def enumerate(seq)
>   i = 0
>   Generator.new {|g|
*     seq.each {|v|
*       g.yield [i, v]
>       i += 1
>     }
>   }
> end
=> nil
> for i, v in enumerate(1..4)
>   puts("#{i}, #{v}")
> end
0, 1
1, 2
2, 3
3, 4
=> #<Generator:0x2955bf8 ...(略)...>

Pythonぽくてなかなか良い。