obj.nil?
またはobj == nil
と両方のメリットは何ですか?
obj.nilを使用する方が良いですか?またはobj == nil
まったく同じです。 外部からの観測可能な影響とまったく同じです(pfff) *
そして両方の利点は何ですか?
マイクロ最適化が好きな場合allオブジェクトはfalse
を除いて.nil?
メッセージにnil
を返しますが、オブジェクトは==
メッセージは、他のオブジェクトとの小さなマイクロ比較を実行して、同じオブジェクトかどうかを判断します。
* コメントを参照してください。
個人的には、object.nil?
のほうが長い行で混乱しにくいので、こちらを好みます。ただし、Railsで作業している場合は、通常、object.blank?
も使用します。これは、変数が空かどうかも確認するためです。
構文とスタイルはさておき、nilをテストするためのさまざまなアプローチがどのように「同じ」かを確認したかったのです。それで、私はいくつかのベンチマークを書いて見て、そこにさまざまな形のnilテストを投げました。
実際の結果は、obj
をnilチェックとして使用することがすべてのケースで最も高速であることを示しました。 obj
は、obj.nil?
をチェックするよりも常に30%以上高速です。
驚くべきことに、obj
はobj == nil
のバリエーションの約3〜4倍の速度でパフォーマンスを発揮します。
パフォーマンス集約型アルゴリズムを200%〜300%高速化したいですか?すべてのobj == null
チェックをobj
に変換します。コードのパフォーマンスをサンドバッグにしたいですか?可能な限りどこでもobj == null
を使用してください。 (冗談です:コードをサンドバッグにしないでください!)。
最終的な分析では、常にobj
を使用します。これは Rubyスタイルガイド ルールでうまくいきます: ブール値を処理しているのでない限り、明示的な非nilチェックを行わないでください。
わかりました。それが結果です。このベンチマークはどのようにまとめられ、どのテストが行われ、結果の詳細はどうなっているのでしょうか。
私が思いついたnilチェックは次のとおりです:
さまざまなRubyテストするタイプを選択しました。タイプに基づいて結果が変化した場合に備えて、これらのタイプはFixnum、Float、FalseClass、TrueClass、String、およびRegexでした。
これらのnilチェック条件を各タイプに使用して、パフォーマンスの点でそれらの間に違いがあるかどうかを確認しました。タイプごとに、nilオブジェクトと非nil値オブジェクトの両方をテストしました(例:1_000_000
、100_000.0
、false
、true
、"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)
2つの操作は非常に異なりますが、少なくとも何かのエッジにいる誰かがオブジェクトの#nil?
メソッドをオーバーライドすることを決定するまで、それらが常に同じ結果を生成すると確信しています。 (1つはObjectから継承された、またはNilClass
でオーバーライドされた#nil?
メソッドを呼び出し、もう1つはnil
シングルトンと比較します。)
疑わしい場合は、実際には3番目の方法で、式の真の値をテストすることをお勧めします。
したがって、式の値がfalseのときにこのDTRTをテストするには、if x
またはif x == nil
ではなくif x.nil?
を使用します。この方法で作業すると、FalseClass#nil?
をtrueと定義するように誘惑されるのを防ぐのにも役立ちます。
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
あなたができるときは、.nil?
をまったく使用していません。
unless obj
// do work
end
.nil?
の使用は実際には遅くなりますが、それほど顕著ではありません。 .nil?
は、そのオブジェクトがnilに等しいかどうかを確認するためのメソッドにすぎませんが、視覚的な魅力と、パフォーマンスにほとんど差がないことを除けば、違いはありません。