2016-10-11 107 views
1

我有一個散列數組,其密鑰爲Date,值爲Integer。 這是一個模擬它的測試代碼。爲什麼Range#include?比運算符大於或小於

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> 

我以爲這兩個表達基本相同。

爲什麼有這麼大的差異?

回答

5

您需要使用Range#cover?而不是Range#include?,並且只計算一次範圍,而不是爲measure的每個元素計算一次。 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 

在執行速度方面,這應該與第二種方法幾乎相同。

一個例子

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} 
相關問題