2016-10-11 59 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} 
相关问题