【发布时间】:2010-12-30 15:29:24
【问题描述】:
使用obj.nil? 或obj == nil 哪个更好,两者有什么好处?
【问题讨论】:
标签: ruby null comparison
使用obj.nil? 或obj == nil 哪个更好,两者有什么好处?
【问题讨论】:
标签: ruby null comparison
就我个人而言,我更喜欢object.nil?,因为它在较长的行中可以减少混淆;但是,如果我在 Rails 中工作,我通常也使用object.blank?,因为它还会检查变量是否为空。
【讨论】:
我发现自己根本不使用.nil?,而你可以这样做:
unless obj
// do work
end
使用.nil? 实际上会更慢,但并不明显。 .nil? 只是一种检查该对象是否等于 nil 的方法,除了视觉吸引力和所需的性能很少之外,没有任何区别。
【讨论】:
false 和 nil。
Some 可能会建议使用 .nil?比简单的比较要慢,仔细想想还是有道理的。
但如果规模和速度不是您关心的问题,那么 .nil 呢?可能更具可读性。
【讨论】:
nil? 和 == 都是实例方法。在那个例子中,性能差距是由于对 .nil? 的无用调用造成的。分支上的方法,但是当您检查对象是否为零时,恕我直言,这应该是几乎相同的计算工作......
使用 obj.nil 更好吗?或 obj == nil
完全一样。从外部观察到的效果完全相同(pfff)*
两者都有什么好处。
如果你喜欢微优化所有对象将返回false到.nil?消息除了对象nil本身,而使用==消息的对象将执行微小的比较
与另一个对象来确定它是否是同一个对象。
* 见 cmets。
【讨论】:
nil? 而不是==,反之亦然。再说一次,仅仅因为你可以并不意味着你应该......
#nil? 方法,在理智的世界中,该方法仅对 NilClass 返回 true,然后将一个对象与 nil 单例进行比较。不完全相同。除非疯狂边缘覆盖nil?,否则它应该会产生相同的结果真值,但即便如此,想象一下有人决定修补FalseClass,以便nil? 是真的......
您可以在 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? 之前?
arr.any?(&:nil?) 的作用?
虽然这两个操作非常不同,但我很确定它们总是会产生相同的结果,至少在某个处于边缘的人决定覆盖 Object 的 #nil? 方法之前。 (一个调用从 Object 继承或在 NilClass 中重写的 #nil? 方法,一个与 nil 单例进行比较。)
我建议,当你有疑问时,你可以采用第三种方法,实际上,只测试表达式的真值。
所以,if x 而不是 if x == nil 或 if x.nil?,以便在表达式值为 false 时进行此测试 DTRT。以这种方式工作还有助于避免诱使某人将FalseClass#nil? 定义为true。
【讨论】:
抛开语法和风格不谈,我想看看测试 nil 的各种方法有多“相同”。所以,我写了一些基准测试,并对其进行了各种形式的 nil 测试。
实际结果表明,使用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.
好的,这就是结果。那么这个基准是如何组合起来的,做了哪些测试,结果的细节是什么?
我想出的零检查是:
objobj.nil?!obj!!objobj == nilobj != 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)
【讨论】: