【问题标题】:obj.nil? vs. obj == nilobj.nil?与 obj == nil
【发布时间】:2010-12-30 15:29:24
【问题描述】:

使用obj.nil?obj == nil 哪个更好,两者有什么好处?

【问题讨论】:

    标签: ruby null comparison


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      我发现自己根本不使用.nil?,而你可以这样做:

      unless obj
        // do work
      end
      

      使用.nil? 实际上会更慢,但并不明显。 .nil? 只是一种检查该对象是否等于 nil 的方法,除了视觉吸引力和所需的性能很少之外,没有任何区别。

      【讨论】:

      • 这种方法存在问题,因为它匹配 falsenil
      • 我想知道“除非obj”是否完整,或者它之后是否应该有一些东西。使用 if obj.nil?,你就知道它是完整的。
      【解决方案3】:

      Some 可能会建议使用 .nil?比简单的比较要慢,仔细想想还是有道理的。

      但如果规模和速度不是您关心的问题,那么 .nil 呢?可能更具可读性。

      【讨论】:

      • 在 Ruby 中 nil?== 都是实例方法。在那个例子中,性能差距是由于对 .nil? 的无用调用造成的。分支上的方法,但是当您检查对象是否为零时,恕我直言,这应该是几乎相同的计算工作......
      【解决方案4】:

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

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

      两者都有什么好处。

      如果你喜欢微优化所有对象将返回false.nil?消息除了对象nil本身,而使用==消息的对象将执行微小的比较 与另一个对象来确定它是否是同一个对象。

      * 见 cmets。

      【讨论】:

      • "完全一样。"除非某人的monkeypatchednil? 而不是==,反之亦然。再说一次,仅仅因为你可以并不意味着你应该......
      • “一模一样”? 嗯。一个调用从 Object 继承的 #nil? 方法,在理智的世界中,该方法仅对 NilClass 返回 true,然后将一个对象与 nil 单例进行比较。不完全相同。除非疯狂边缘覆盖nil?,否则它应该会产生相同的结果真值,但即便如此,想象一下有人决定修补FalseClass,以便nil? 是真的......
      • @Andrew @DigitalRoss 好的,好的,我改变了短语并提到了你的 cmets :) 有人认为这是 Ruby 的主要问题;对于一个大型项目,您必须添加测试用例来测试这些愚蠢的事情之一。
      【解决方案5】:

      您可以在 nil? 上使用 Symbol#to_proc,而在 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
      

      【讨论】:

      【解决方案6】:

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

      虽然这两个操作非常不同,但我很确定它们总是会产生相同的结果,至少在某个处于边缘的人决定覆盖 Object 的 #nil? 方法之前。 (一个调用从 Object 继承或在 NilClass 中重写的 #nil? 方法,一个与 nil 单例进行比较。)

      我建议,当你有疑问时,你可以采用第三种方法,实际上,只测试表达式的真值。

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

      【讨论】:

        【解决方案7】:

        抛开语法和风格不谈,我想看看测试 nil 的各种方法有多“相同”。所以,我写了一些基准测试,并对其进行了各种形式的 nil 测试。

        TL;DR - 结果优先

        实际结果表明,使用obj作为nil检查是所有情况下最快的。 obj 始终比检查 obj.nil? 快 30% 或更多。

        令人惊讶的是,obj 的执行速度大约是 obj == nil 的变体的 3-4 倍,这似乎是一种惩罚性的性能损失。

        想要将性能密集型算法的速度提高 200%-300%?将所有obj == nil 检查转换为obj。想要沙袋你的代码的性能?尽可能使用obj == nil。 (开个玩笑:不要沙包你的代码!)。

        归根结底,始终使用obj。这符合Ruby Style Guide 规则:Don't do explicit non-nil checks unless you're dealing with boolean values.

        基准条件

        好的,这就是结果。那么这个基准是如何组合起来的,做了哪些测试,结果的细节是什么?

        我想出的零检查是:

        • obj
        • obj.nil?
        • !obj
        • !!obj
        • obj == nil
        • obj != nil

        我选择了各种 Ruby 类型进行测试,以防结果因类型而异。这些类型是FixnumFloatFalseClassTrueClassStringRegex

        我对每种类型都使用了这些 nil 检查条件,以查看它们之间是否存在性能差异。对于每种类型,我都测试了 nil 对象和非 nil 值对象(例如1_000_000100_000.0falsetrue"string"/\w/),看看检查是否有区别对于 nil 对象上的 nil 与非 nil 对象上的比较。

        基准

        除了所有这些,这里是基准代码:

        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 版本的结果显示在此处(可在the gist 获得:

        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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-13
          • 2023-04-02
          • 1970-01-01
          • 2010-11-24
          • 1970-01-01
          • 1970-01-01
          • 2012-06-28
          • 2021-09-28
          相关资源
          最近更新 更多