2016-10-11 37 views
1

私はキーがDateで、値がIntegerであるハッシュの配列を持っています。 これをエミュレートするテストコードです。なぜRange#が含まれていますか?演算子の大小よりもはるかに遅い

hashes = 2000.times.map do |i| 
    [Date.new(2017) - i.days, rand(100)] 
end.to_h 

特定の期間の値を取得したいと考えています。 最初はRange#include?と書いたが、それはかなり遅かった。

Benchmark.measure do 
    hashes.select{|k,v| (Date.new(2012,3,3)..Date.new(2012,6,10)).include?(k)} 
end 

#<Benchmark::Tms:0x007fd16479bed0 @label="", @real=2.9242447479628026, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=2.920000000000016, @total=2.920000000000016> 

オペレータが単純か大きいか小さいかによって、60倍も速くなりました。

Benchmark.measure do 
    hashes.select{|k,v| k >= Date.new(2012,3,3) && k <= Date.new(2012,6,10)} 
end 

#<Benchmark::Tms:0x007fd162b61670 @label="", @real=0.05436371313408017, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.05000000000001137, @total=0.05000000000001137> 

私はこれらの2つの表現が基本的に同じだと思いました。

なぜ大きな違いがありますか?

答えて

5

Range#include?の代わりにRange#cover?を使用し、measureの要素ごとに1回ではなく1回だけ範囲を計算する必要があります。 cover?はブロック変数kを範囲の終点と比較します。 include?(日付などの非数値オブジェクトの場合)は、範囲内の各要素をブロック変数と比較し、一致するものが見つかるか、または一致するものがないと判断します(Array#include?と同様)。

はまた、あなたがhashes(ハッシュ)の各要素の最初で唯一のキーを検討したいので、そのハッシュがhであれば、最初のキーと値のペアはh.firstであり、そのペアのキーがh.first.firstです。

require 'date' 

Benchmark.measure do 
    r = Date.new(2012,3,3)..Date.new(2012,6,10) 
    hashes.select{|h| r.cover? h.first.first } 
end 

これは実行速度の点で2番目の方法とほぼ同じです。

hashes = [{ Date.new(2012,3,1)=>1 }, { Date.new(2012,4,20)=>2 }, 
      { Date.new(2012,6,10)=>3 }, { Date.new(2012,6,11)=>4 }] 
    #=> [{#<Date: 2012-03-01 ((2455988j,0s,0n),+0s,2299161j)>=>1}, 
    # {#<Date: 2012-04-20 ((2456038j,0s,0n),+0s,2299161j)>=>2}, 
    # {#<Date: 2012-06-10 ((2456089j,0s,0n),+0s,2299161j)>=>3}, 
    # {#<Date: 2012-06-11 ((2456090j,0s,0n),+0s,2299161j)>=>4}] 

r = Date.new(2012,3,3)..Date.new(2012,6,10) 
hashes.select{|h| r.cover? h.first.first } 
    #=> {#<Date: 2012-04-20 ((2456038j,0s,0n),+0s,2299161j)>=>2, 
    # #<Date: 2012-06-10 ((2456089j,0s,0n),+0s,2299161j)>=>3} 
関連する問題