From: 大田黒俊夫 <toshio.otaguro / gmail.com>
Subject: [ruby-list:46378] 最も低コストの方法は?
Date: Fri, 11 Sep 2009 22:27:11 +0900

るびきちです。

> 文字列を要素とする配列 r0 があったとします.この配列の要素が全て空の場合を調べたいのですが,
> 以下の3つのいずれの方法が最もコスト(メモリ,マシンサイクル)がかからないでしょうか?
> また,これら以外にさらに低コストの方法がありますか?なお, r0 の要素数は事前にはわからず,
> またこの処理はあるループの中で多数回繰り返されるとします.

プロファイリングを取った上で、本当にこの部分がボトルネックですか?念の為。
とりあえず、1000要素の配列において処理を100回繰り返すベンチマークをとってみました。
データによって異なることがわかります。

# 「空」を空文字列で表現している場合です。

require 'benchmark'
Benchmark.bm(10) do |b|         # ひとつの要素が異なる場合
  n = 100
  r0 = Array.new(1000, ""); r0[55] = "a"
  empty_array = Array.new(r0.size, "")
  r0 == empty_array                                                                         # => false
  r0.uniq == [""]                                                                           # => false
  allempty = 0; r0.each{|x| break if (allempty += (x.empty? ? 0 : 1)) > 0 }; allempty == 0  # => false
  r0.all?{|e| e==""}                                                                        # => false
  b.report("empty_ary"  ) { n.times{ r0 == empty_array }}
  b.report("uniq")        { n.times{ r0.uniq == [""] }}
  b.report("each")        { n.times{ allempty = 0; r0.each{|x| break if (allempty += (x.empty? ? 0 : 1)) > 0 }}}
  b.report("all")         { n.times{ r0.all?{|e| e==""}}}
end
Benchmark.bm(10) do |b|         # すべての要素が空文字列の場合
  n = 100
  r0 = Array.new(1000, "")
  empty_array = Array.new(r0.size, "")
  r0 == empty_array                                                                         # => true
  r0.uniq == [""]                                                                           # => true
  allempty = 0; r0.each{|x| break if (allempty += (x.empty? ? 0 : 1)) > 0 }; allempty == 0  # => true
  r0.all?{|e| e==""}                                                                        # => true
  b.report("empty_ary"  ) { n.times{ r0 == empty_array }}
  b.report("uniq")        { n.times{ r0.uniq == [""] }}
  b.report("each")        { n.times{ allempty = 0; r0.each{|x| break if (allempty += (x.empty? ? 0 : 1)) > 0 }}}
  b.report("all")         { n.times{ r0.all?{|e| e==""}}}
end
RUBY_VERSION                    # => "1.8.8"
# >>                 user     system      total        real
# >> empty_ary   0.000000   0.000000   0.000000 (  0.001170)
# >> uniq        0.030000   0.000000   0.030000 (  0.037613)
# >> each        0.000000   0.000000   0.000000 (  0.008068)
# >> all         0.010000   0.000000   0.010000 (  0.011180)
# >>                 user     system      total        real
# >> empty_ary   0.030000   0.000000   0.030000 (  0.023194)
# >> uniq        0.020000   0.000000   0.020000 (  0.039152)
# >> each        0.130000   0.000000   0.130000 (  0.154927)
# >> all         0.110000   0.000000   0.110000 (  0.139925)


RUBY_VERSION                    # => "1.9.2"
# >>                 user     system      total        real
# >> empty_ary   0.000000   0.000000   0.000000 (  0.001076)
# >> uniq        0.020000   0.000000   0.020000 (  0.027756)
# >> each        0.010000   0.000000   0.010000 (  0.002645)
# >> all         0.000000   0.000000   0.000000 (  0.008598)
# >>                 user     system      total        real
# >> empty_ary   0.010000   0.000000   0.010000 (  0.019093)
# >> uniq        0.040000   0.000000   0.040000 (  0.030770)
# >> each        0.040000   0.000000   0.040000 (  0.049135)
# >> all         0.060000   0.000000   0.060000 (  0.079198)

================
もし、「空」をnilで表現することが可能ならば、 Enumerable#none? というメソッドが使えます。
これが最高速です。

[nil, nil, nil].none?  # => true
[nil, nil, "a"].none?  # => false
["a", "b", "c"].none?  # => false

[nil, nil, nil].any?   # => false
[nil, nil, "a"].any?   # => true
["a", "b", "c"].any?   # => true

--
rubikitch
Blog: http://d.hatena.ne.jp/rubikitch/
Site: http://www.rubyist.net/~rubikitch/
Twit: http://twitter.com/rubikitch/
『Ruby逆引きハンドブック』 http://d.hatena.ne.jp/rubikitch/20090525/rubybook