enumerator

enumerator - #生存戦略 、それは - subtechを読んで、たしかRuby 1.9では以下のように書けるとどこかで読んだ気がしたので実際に試したらちゃんと動いた。これはかっこいい。

[1, 1, 1].map.with_index {|e, i| e + i} 
# => [1, 2, 3]

なんとなくeach_with_indexよりもeach.with_indexの方が重そうに感じる(見た目が多段だから?)けど、これは実際にベンチマークとってみたい。

require 'benchmark'
COUNT = 1000
range = (1..1000)
array = range.to_a
Benchmark.bmbm do |x|
  x.report("range.e_w_i") do
    COUNT.times do
      range.each_with_index do |e, i|
	e + i
      end
    end
  end

  x.report("range.e.w_i") do
    COUNT.times do
      range.each.with_index do |e, i|
	e + i
      end
    end
  end

  x.report("array.e_w_i") do
    COUNT.times do
      array.each_with_index do |e, i|
	e + i
      end
    end
  end

  x.report("array.e.w_i") do
    COUNT.times do
      array.each.with_index do |e, i|
	e + i
      end
    end
  end
end

まずは会社のCygwin環境にて。

ruby 1.9.0 (2006-08-04) [i386-cygwin]
Rehearsal -----------------------------------------------
range.e_w_i   3.125000   0.015000   3.140000 (  3.168000)
range.e.w_i   3.110000   0.000000   3.110000 (  3.127000)
array.e_w_i   3.140000   0.000000   3.140000 (  3.159000)
array.e.w_i   3.094000   0.000000   3.094000 (  3.124000)
------------------------------------- total: 12.484000sec

                  user     system      total        real
range.e_w_i   3.125000   0.000000   3.125000 (  3.157000)
range.e.w_i   3.078000   0.016000   3.094000 (  3.102000)
array.e_w_i   3.141000   0.000000   3.141000 (  3.162000)
array.e.w_i   3.062000   0.000000   3.062000 (  3.096000)       

あまり差がない、というかむしろeach.with_indexの方が速い様に見える。
自宅のFreeBSDマシンでも試すべきかと思いつつ、なんかピンときたので、COUNTを100000、rangeを(1..100)に変えて再度試した。

ruby 1.9.0 (2006-08-04) [i386-cygwin]
Rehearsal -----------------------------------------------
range.e_w_i   3.360000   0.000000   3.360000 (  3.404000)
range.e.w_i   3.750000   0.015000   3.765000 (  3.786000)
array.e_w_i   3.328000   0.000000   3.328000 (  3.359000)
array.e.w_i   3.703000   0.000000   3.703000 (  3.742000)
------------------------------------- total: 14.156000sec

                  user     system      total        real
range.e_w_i   3.375000   0.000000   3.375000 (  3.382000)
range.e.w_i   3.828000   0.000000   3.828000 (  3.840000)
array.e_w_i   3.359000   0.000000   3.359000 (  3.384000)
array.e.w_i   3.703000   0.000000   3.703000 (  3.750000)

each_with_indexの方が速くなった。これはEnumerable::Enumeratorオブジェクトの生成コストの問題のような気がする。
適当なベンチマークを適当なマシン上で動かした結果なので信頼度は低いが、思ったより強烈な差は確認できなかったので、どちらを使ってもそんなに変わんないんじゃないかなあ、という結論にしてみる。一安心したところで仕事に戻ります。