rubyのselectやmapなどの計算量について


rubyで配列やHashなどのselectやmapなどを繰り返し呼んだ時ループは何回行われるのでしょうか？

例えば下記のコードでは

[code]

    (1..100)
      .select{|val| val % 2 == 0}
      .map{|val| val.to_s}
      .join " "
    
[/code]

行われるループは３回、あるいは１回ループで３つの処理をするのでしょうか？

言語によっては遅延評価をしてループの回数を減らしてくれることもあるのでベンチマークをとって見たのですが、非常に混乱する結果でした。

試したのは下記のベンチです。比較のためeach_with_objectを使った処理も入れて見ました。

[code]

    Benchmark.ips do |x|
      x.report("repeat map") do
        (1..10000)
          .map{|val| val.to_s}
          .map{|val| val.to_i}
          .map{|val| val.to_s}
          .map{|val| val.to_i}
      end
    
      x.report("each_with_object") do
        (1..10000).each_with_object([]) do |val, res|
          val = val.to_s
          val = val.to_i
          val = val.to_s
          val = val.to_i
        end
      end
    
      x.report("repeat each") do
        range = (1..10000)
        range.each do |val|
          val = val.to_s
        end
        range.each do |val|
          val = val.to_i
        end
        range.each do |val|
          val = val.to_s
        end
        range.each do |val|
          val = val.to_i
        end
      end
      x.compare!
    end
    
[/code]

結果はこんな感じです（数が多いほど早いです）。

[code]

          repeat map    140.435  (± 8.5%) i/s -    700.000  in   5.023912s
    each_with_object    176.636  (± 5.1%) i/s -    884.000  in   5.017814s
         repeat each    186.010  (± 5.4%) i/s -    936.000  in   5.048520s
    
[/code]

`repeat map`が繰り返しループされるのかわからないですが、`repeat
each`が`each_with_object`とほぼ変わらないのが解せませんでした。多分、私のテストしたコードになにか問題があるのだと思いますが・・・

配列やHashに対して気軽に、繰り返しselectやreject、mapなどを呼んでも良いのか気になったので投稿しました。

よろしくお願いします。




`obj.map{...}.map{...}.map{...}` とした場合は各 `map` が全要素を処理した後に次の `map` に渡します。

Ruby2.0 から `Enumerable#lazy` が導入され、`obj.lazy.map{...}.map{...}.map{...}`
とすることで、要素毎に各 `map` を処理するようになります。

<http://magazine.rubyist.net/?0041-200Special-lazy> が参考になると思います。

