obj.nil?与obj == nil


Answers:


49

使用obj.nil更好吗?或obj == nil

完全一样。从外部(pfff)具有完全相同的可观察效果*

两者都有什么好处。

如果您喜欢微优化,则除对象本身外,所有对象都会返回false.nil?消息中nil,而使用==消息的对象将与其他对象进行微小的微比较,以确定它是否是同一对象。

*查看评论。


“完全一样。” 除非某人的猴子修补了 nil?,否则没有==。再说一遍,只是因为你不能意味着你不应该……
安德鲁·格林

6
“一模一样”?嗯 一个调用#nil?从Object继承的方法,在一个理智的世界中,该方法仅对会返回true NilClass,然后将一个对象与nil单例进行比较。不完全一样。除非疯子边缘被覆盖nil?,否则它应该产生相同的结果真值,但即使如此,也可以想象有人决定打补丁FalseClass以便做到这nil?一点……
DigitalRoss11年

2
@Andrew @DigitalRoss好吧,好吧,我更改了短语,并提到了您的评论:)有人认为这是Ruby的主要问题。对于一个大型项目,您必须添加测试用例以测试没有一项愚蠢的事情。
OscarRyz 2011年

11

就我个人而言,我更喜欢这样,object.nil?因为它可以减少较长行的混乱;但是,object.blank?如果我在Rails中工作,通常也会使用该方法,因为它还会检查变量是否为空。


8

除了语法和样式,我想看看测试nil的各种方法有多么“相同”。因此,我编写了一些基准测试,并对其进行了各种形式的零测试。

TL; DR-结果优先

实际结果表明,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类型进行测试,以防结果根据类型而改变。这些类型的人FixnumFloatFalseClassTrueClassString,和Regex

我在每种类型上都使用了这些nil检查条件,以查看它们之间在性能方面是否存在差异。对于每种类型的,我测试都为零的对象和非零值的对象(例如1_000_000100_000.0falsetrue"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)

5

在许多情况下,都不是,只需测试布尔真值

尽管这两个操作有很大的不同,但我敢肯定,它们将始终产生相同的结果,至少直到某人陷入困境时才决定重写Object的#nil?方法。(一个调用#nil?从Object继承或覆盖的方法NilClass,另一个与nil单例进行比较。)

我建议,当您有疑问时,您实际上走了第三条路,只是测试表达式的真值。

因此,if x而不是if x == nilor if x.nil?,以便在表达式值为false时进行此DTRT测试。以这种方式工作也可能有助于避免诱使某人定义FalseClass#nil?true



0

我发现自己什么.nil?时候都没用:

unless obj
  // do work
end

实际上使用起来较慢,.nil?但并不明显。.nil?只是一种检查该对象是否等于nil的方法,除了视觉吸引力和几乎没有性能要求外,没有其他区别。


6
这种方法的问题,因为它匹配false以及nil
horseyguy

我想知道“除非obj”是否完整,或者是否应该包含一些内容。如果使用obj.nil ?,您知道它已经完成。
安德鲁·格林

-1

有人可能建议使用.nil?比简单的比较慢,这在您考虑时很有意义。

但是,如果您不关心规模和速度,那么.nil?也许更具可读性。


1
在Ruby中nil?==它们都是实例方法。在该示例中,性能差距是由于对.nil?的无用调用导致的。分支上的方法,但是当您检查对象是否为零时,恕我直言,它应该几乎是相同的计算工作量……
Andrea Salicetti 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.