web-dev-qa-db-ja.com

obj.nil?対obj == nil

obj.nil?またはobj == nilと両方のメリットは何ですか?

67
Danish Khan

obj.nilを使用する方が良いですか?またはobj == nil

まったく同じです。 外部からの観測可能な影響とまったく同じです(pfff) *

そして両方の利点は何ですか?

マイクロ最適化が好きな場合allオブジェクトはfalseを除いて.nil?メッセージにnilを返しますが、オブジェクトは==メッセージは、他のオブジェクトとの小さなマイクロ比較を実行して、同じオブジェクトかどうかを判断します。

* コメントを参照してください。

46
OscarRyz

個人的には、object.nil?のほうが長い行で混乱しにくいので、こちらを好みます。ただし、Railsで作業している場合は、通常、object.blank?も使用します。これは、変数が空かどうかも確認するためです。

11
Topher Fangio

構文とスタイルはさておき、nilをテストするためのさまざまなアプローチがどのように「同じ」かを確認したかったのです。それで、私はいくつかのベンチマークを書いて見て、そこにさまざまな形のnilテストを投げました。

TL; DR-最初の結果

実際の結果は、objをnilチェックとして使用することがすべてのケースで最も高速であることを示しました。 objは、obj.nil?をチェックするよりも常に30%以上高速です。

驚くべきことに、objobj == nilのバリエーションの約3〜4倍の速度でパフォーマンスを発揮します。

パフォーマンス集約型アルゴリズムを200%〜300%高速化したいですか?すべてのobj == nullチェックをobjに変換します。コードのパフォーマンスをサンドバッグにしたいですか?可能な限りどこでもobj == nullを使用してください。 (冗談です:コードをサンドバッグにしないでください!)。

最終的な分析では、常にobjを使用します。これは Rubyスタイルガイド ルールでうまくいきます: ブール値を処理しているのでない限り、明示的な非nilチェックを行わないでください。

ベンチマーク条件

わかりました。それが結果です。このベンチマークはどのようにまとめられ、どのテストが行​​われ、結果の詳細はどうなっているのでしょうか。

私が思いついたnilチェックは次のとおりです:

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

さまざまなRubyテストするタイプを選択しました。タイプに基づいて結果が変化した場合に備えて、これらのタイプはFixnum、Float、FalseClass、TrueClass、String、およびRegexでした。

これらの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)
5
Michael Gaskill

多くの場合、どちらでもない真偽値をテストするだけです

2つの操作は非常に異なりますが、少なくとも何かのエッジにいる誰かがオブジェクトの#nil?メソッドをオーバーライドすることを決定するまで、それらが常に同じ結果を生成すると確信しています。 (1つはObjectから継承された、またはNilClassでオーバーライドされた#nil?メソッドを呼び出し、もう1つはnilシングルトンと比較します。)

疑わしい場合は、実際には3番目の方法で、式の真の値をテストすることをお勧めします。

したがって、式の値がfalseのときにこのDTRTをテストするには、if xまたはif x == nilではなくif x.nil?を使用します。この方法で作業すると、FalseClass#nil?trueと定義するように誘惑されるのを防ぐのにも役立ちます。

4
DigitalRoss

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
3
Andrew Grimm

あなたができるときは、.nil?をまったく使用していません。

unless obj
  // do work
end

.nil?の使用は実際には遅くなりますが、それほど顕著ではありません。 .nil?は、そのオブジェクトがnilに等しいかどうかを確認するためのメソッドにすぎませんが、視覚的な魅力と、パフォーマンスにほとんど差がないことを除けば、違いはありません。

0
Garrett