ruby 'Range#include?'和'Range#cover?'之间的区别是什么?

mrphzbgm  于 2023-02-12  发布在  Ruby
关注(0)|答案(4)|浏览(278)

编辑修正了toro2k的评论。

Range#include?Range#cover?看起来是不同的,正如在源代码1、2中所看到的,并且它们在效率上是不同的。

t = Time.now
500000.times do
    ("a".."z").include?("g")
end
puts Time.now - t    # => 0.504382493

t = Time.now
500000.times do
    ("a".."z").cover?("g")
end
puts Time.now - t    # => 0.454867868

从源代码来看,Range#include?似乎比Range#cover?更复杂,为什么Range#include?不能简单地作为Range#cover?的别名呢?

slhcrj9b

slhcrj9b1#

这两个方法的设计目的是做两件略有不同的事情。它们的内部实现也非常不同。您可以查看文档中的源代码,发现.include?.cover?做得更多
.cover?方法与Comparable模块相关,它检查一个项目是否适合排序列表中的端点。即使该项目不在Range隐含的集合中,它也将返回true。
.include?方法与Enumerable模块有关,它检查一个项是否真的在Range所隐含的完整集合中。对数值做了一些微调-整数范围被视为包括所有隐含的Float值(我不知道为什么)。
以下示例可能会有所帮助:

('a'..'z').cover?('yellow')
# => true

('a'..'z').include?('yellow')
# => false

('yellaa'..'yellzz').include?('yellow')
 => true

另外,如果你尝试

('aaaaaa'..'zzzzzz').include?('yellow')

你应该注意到它需要的时间比

('aaaaaa'..'zzzzzz').cover?('yellow')
zqry0prt

zqry0prt2#

主要区别是include检查的是object是否为range元素之一,cover返回的是object是否在edge元素之间,可以看到:

('a'..'z').include?('cc')     #=> false
('a'..'z').cover?('cc')       #=> true
u0sqgete

u0sqgete3#

date_range = {:start_date => (DateTime.now + 1.days).to_date, :end_date => (DateTime.now + 10.days).to_date}                
date_range_to_check_for_coverage = {:start_date => (DateTime.now + 5.days).to_date, :end_date => (DateTime.now + 7.days).to_date}                

(date_range[:start_date]..date_range[:end_date]).include?((DateTime.now + 5.days).to_date)                
#true        
(date_range[:start_date]..date_range[:end_date]).cover?((DateTime.now + 5.days).to_date)                
#true        
(date_range[:start_date]..date_range[:end_date]).include?(date_range_to_check_for_coverage[:start_date]..date_range_to_check_for_coverage[:end_date])                
#true        
(date_range[:start_date]..date_range[:end_date]).cover?(date_range_to_check_for_coverage[:start_date]..date_range_to_check_for_coverage[:end_date])                
#false

最后一行不是应该返回true吗?
我问这个问题的原因是当我用include?代替cover?时,rubocop标记了一个冲突。很明显,我的逻辑(检查范围是否包含在另一个范围中)不适用于cover?。

7ajki6be

7ajki6be4#

cover?include?之间存在巨大的性能差异:使用日期范围时要特别小心

基于上述理由:cover?只检查参数是否在范围的开始和结束之间;在include?中,你检查你的参数**是否在范围内,这涉及到检查范围内的每一个元素,而不仅仅是开始/结束。
让我们运行一个简单的基准测试。

date_range = Date.parse("1990-01-01")..Date.parse("2023-01-01");

target_date = Date.parse("2023-01-01"); 

iterations = 1000;

Benchmark.bmbm do |bm|

  bm.report("using include") { iterations.times { date_range.include?(target_date) } }

  bm.report("using cover") { iterations.times { date_range.cover?(target_date) } }

end

结果:

Rehearsal -------------------------------------------------
using include   5.466448   0.071381   5.537829 (  5.578123)
using cover     0.000272   0.000003   0.000275 (  0.000279)
---------------------------------------- total: 5.538104sec

                    user     system      total        real
using include   5.498635   0.046663   5.545298 (  5.557880)
using cover     0.000284   0.000000   0.000284 (  0.000280)

如您所见,使用#cover?是 * 即时的 *;您可以在0.000ms内获得结果。
但是,使用#include?几乎需要 *5.5秒 * 才能获得相同的结果。
仔细选择。

相关问题