使用它更好obj.nil?
还是obj == nil
两者都有好处?
Answers:
使用obj.nil更好吗?或obj == nil
完全一样。从外部(pfff)具有完全相同的可观察效果*
两者都有什么好处。
如果您喜欢微优化,则除对象本身外,所有对象都会返回false
到.nil?
消息中nil
,而使用==
消息的对象将与其他对象进行微小的微比较,以确定它是否是同一对象。
*查看评论。
#nil?
从Object继承的方法,在一个理智的世界中,该方法仅对会返回true NilClass
,然后将一个对象与nil
单例进行比较。不完全一样。除非疯子边缘被覆盖nil?
,否则它应该产生相同的结果真值,但即使如此,也可以想象有人决定打补丁FalseClass
以便做到这nil?
一点……
除了语法和样式,我想看看测试nil的各种方法有多么“相同”。因此,我编写了一些基准测试,并对其进行了各种形式的零测试。
实际结果表明,obj
在所有情况下,将其作为零检查是最快的。 obj
始终比check快30%或更多obj.nil?
。
令人惊讶的是,其obj
执行速度是的变体的3-4倍obj == nil
,因此似乎会受到惩罚。
是否想将性能密集型算法提高200%-300%?将所有obj == nil
支票转换为obj
。是否想破坏您的代码的性能?尽可能使用obj == nil
所有地方。(开个玩笑:不要给您的代码丢包!)。
归根结底,请务必使用obj
。这符合Ruby样式指南的规则:除非您要处理布尔值,否则请不要进行显式的非nil检查。
好的,这些就是结果。那么,该基准如何组合在一起,进行了哪些测试,结果的细节是什么?
我想出的零检查是:
obj
obj.nil?
!obj
!!obj
obj == nil
obj != nil
我选择了各种Ruby类型进行测试,以防结果根据类型而改变。这些类型的人Fixnum
,Float
,FalseClass
,TrueClass
,String
,和Regex
。
我在每种类型上都使用了这些nil检查条件,以查看它们之间在性能方面是否存在差异。对于每种类型的,我测试都为零的对象和非零值的对象(例如1_000_000
,100_000.0
,false
,true
,"string"
,和/\w/
),以查看是否有一个在检查的对象上零的差是零与一个对象,这不是零上。
有了所有这些,下面是基准代码:
require 'benchmark'
nil_obj = nil
N = 10_000_000
puts RUBY_DESCRIPTION
[1_000_000, 100_000.0, false, true, "string", /\w/].each do |obj|
title = "#{obj} (#{obj.class.name})"
puts "============================================================"
puts "Running tests for obj = #{title}"
Benchmark.bm(15, title) do |x|
implicit_obj_report = x.report("obj:") { N.times { obj } }
implicit_nil_report = x.report("nil_obj:") { N.times { nil_obj } }
explicit_obj_report = x.report("obj.nil?:") { N.times { obj.nil? } }
explicit_nil_report = x.report("nil_obj.nil?:") { N.times { nil_obj.nil? } }
not_obj_report = x.report("!obj:") { N.times { !obj } }
not_nil_report = x.report("!nil_obj:") { N.times { !nil_obj } }
not_not_obj_report = x.report("!!obj:") { N.times { !!obj } }
not_not_nil_report = x.report("!!nil_obj:") { N.times { !!nil_obj } }
equals_obj_report = x.report("obj == nil:") { N.times { obj == nil } }
equals_nil_report = x.report("nil_obj == nil:") { N.times { nil_obj == nil } }
not_equals_obj_report = x.report("obj != nil:") { N.times { obj != nil } }
not_equals_nil_report = x.report("nil_obj != nil:") { N.times { nil_obj != nil } }
end
end
结果很有趣,因为Fixnum,Float和String类型的性能几乎相同,而Regex几乎相同,而FalseClass和TrueClass的执行速度要快得多。对MRI版本1.9.3、2.0.0、2.1.5和2.2.5进行了测试,各个版本的比较结果非常相似。MRI 2.2.5版本的结果显示在此处(要点如下:
ruby 2.2.5p319 (2016-04-26 revision 54774) [x86_64-darwin14]
============================================================
Running tests for obj = 1000000 (Fixnum)
user system total real
obj: 0.970000 0.000000 0.970000 ( 0.987204)
nil_obj: 0.980000 0.010000 0.990000 ( 0.980796)
obj.nil?: 1.250000 0.000000 1.250000 ( 1.268564)
nil_obj.nil?: 1.280000 0.000000 1.280000 ( 1.287800)
!obj: 1.050000 0.000000 1.050000 ( 1.064061)
!nil_obj: 1.070000 0.000000 1.070000 ( 1.170393)
!!obj: 1.110000 0.000000 1.110000 ( 1.122204)
!!nil_obj: 1.120000 0.000000 1.120000 ( 1.147679)
obj == nil: 2.110000 0.000000 2.110000 ( 2.137807)
nil_obj == nil: 1.150000 0.000000 1.150000 ( 1.158301)
obj != nil: 2.980000 0.010000 2.990000 ( 3.041131)
nil_obj != nil: 1.170000 0.000000 1.170000 ( 1.203015)
============================================================
Running tests for obj = 100000.0 (Float)
user system total real
obj: 0.940000 0.000000 0.940000 ( 0.947136)
nil_obj: 0.950000 0.000000 0.950000 ( 0.986488)
obj.nil?: 1.260000 0.000000 1.260000 ( 1.264953)
nil_obj.nil?: 1.280000 0.000000 1.280000 ( 1.306817)
!obj: 1.050000 0.000000 1.050000 ( 1.058924)
!nil_obj: 1.070000 0.000000 1.070000 ( 1.096747)
!!obj: 1.100000 0.000000 1.100000 ( 1.105708)
!!nil_obj: 1.120000 0.010000 1.130000 ( 1.132248)
obj == nil: 2.140000 0.000000 2.140000 ( 2.159595)
nil_obj == nil: 1.130000 0.000000 1.130000 ( 1.151257)
obj != nil: 3.010000 0.000000 3.010000 ( 3.042263)
nil_obj != nil: 1.170000 0.000000 1.170000 ( 1.189145)
============================================================
Running tests for obj = false (FalseClass)
user system total real
obj: 0.930000 0.000000 0.930000 ( 0.933712)
nil_obj: 0.950000 0.000000 0.950000 ( 0.973776)
obj.nil?: 1.250000 0.000000 1.250000 ( 1.340943)
nil_obj.nil?: 1.270000 0.010000 1.280000 ( 1.282267)
!obj: 1.030000 0.000000 1.030000 ( 1.039532)
!nil_obj: 1.060000 0.000000 1.060000 ( 1.068765)
!!obj: 1.100000 0.000000 1.100000 ( 1.111930)
!!nil_obj: 1.110000 0.000000 1.110000 ( 1.115355)
obj == nil: 1.110000 0.000000 1.110000 ( 1.121403)
nil_obj == nil: 1.100000 0.000000 1.100000 ( 1.114550)
obj != nil: 1.190000 0.000000 1.190000 ( 1.207389)
nil_obj != nil: 1.140000 0.000000 1.140000 ( 1.181232)
============================================================
Running tests for obj = true (TrueClass)
user system total real
obj: 0.960000 0.000000 0.960000 ( 0.964583)
nil_obj: 0.970000 0.000000 0.970000 ( 0.977366)
obj.nil?: 1.260000 0.000000 1.260000 ( 1.265229)
nil_obj.nil?: 1.270000 0.010000 1.280000 ( 1.283342)
!obj: 1.040000 0.000000 1.040000 ( 1.059689)
!nil_obj: 1.070000 0.000000 1.070000 ( 1.068290)
!!obj: 1.120000 0.000000 1.120000 ( 1.154803)
!!nil_obj: 1.130000 0.000000 1.130000 ( 1.155932)
obj == nil: 1.100000 0.000000 1.100000 ( 1.102394)
nil_obj == nil: 1.130000 0.000000 1.130000 ( 1.160324)
obj != nil: 1.190000 0.000000 1.190000 ( 1.202544)
nil_obj != nil: 1.200000 0.000000 1.200000 ( 1.200812)
============================================================
Running tests for obj = string (String)
user system total real
obj: 0.940000 0.000000 0.940000 ( 0.953357)
nil_obj: 0.960000 0.000000 0.960000 ( 0.962029)
obj.nil?: 1.290000 0.010000 1.300000 ( 1.306233)
nil_obj.nil?: 1.240000 0.000000 1.240000 ( 1.243312)
!obj: 1.030000 0.000000 1.030000 ( 1.046630)
!nil_obj: 1.060000 0.000000 1.060000 ( 1.123925)
!!obj: 1.130000 0.000000 1.130000 ( 1.144168)
!!nil_obj: 1.130000 0.000000 1.130000 ( 1.147330)
obj == nil: 2.320000 0.000000 2.320000 ( 2.341705)
nil_obj == nil: 1.100000 0.000000 1.100000 ( 1.118905)
obj != nil: 3.040000 0.010000 3.050000 ( 3.057040)
nil_obj != nil: 1.150000 0.000000 1.150000 ( 1.162085)
============================================================
Running tests for obj = (?-mix:\w) (Regexp)
user system total real
obj: 0.930000 0.000000 0.930000 ( 0.939815)
nil_obj: 0.960000 0.000000 0.960000 ( 0.961852)
obj.nil?: 1.270000 0.000000 1.270000 ( 1.284321)
nil_obj.nil?: 1.260000 0.000000 1.260000 ( 1.275042)
!obj: 1.040000 0.000000 1.040000 ( 1.042543)
!nil_obj: 1.040000 0.000000 1.040000 ( 1.047280)
!!obj: 1.120000 0.000000 1.120000 ( 1.128137)
!!nil_obj: 1.130000 0.000000 1.130000 ( 1.138988)
obj == nil: 1.520000 0.010000 1.530000 ( 1.529547)
nil_obj == nil: 1.110000 0.000000 1.110000 ( 1.125693)
obj != nil: 2.210000 0.000000 2.210000 ( 2.226783)
nil_obj != nil: 1.170000 0.000000 1.170000 ( 1.169347)
您可以在上使用Symbol#to_proc nil?
,而在上则不可行x == nil
。
arr = [1, 2, 3]
arr.any?(&:nil?) # Can be done
arr.any?{|x| x == nil} # More verbose, and I suspect is slower on Ruby 1.9.2
! arr.all? # Check if any values are nil or false
&:
运算符之前添加运算符nil?
?
arr.any?(&:nil?)
?
有人可能建议使用.nil?比简单的比较慢,这在您考虑时很有意义。
但是,如果您不关心规模和速度,那么.nil?也许更具可读性。
nil?
,==
它们都是实例方法。在该示例中,性能差距是由于对.nil?的无用调用导致的。分支上的方法,但是当您检查对象是否为零时,恕我直言,它应该几乎是相同的计算工作量……
nil?
,否则没有==
。再说一遍,只是因为你不能意味着你不应该……