Лучше ли использовать obj.nil?
или obj == nil
и каковы преимущества обоих?
Obj.nil? vs. obj == nil
Ответ 1
Лучше ли использовать obj.nil? или obj == nil
Это точно то же самое. Он имеет те же самые наблюдаемые эффекты извне (pfff) *
и каковы преимущества обоих.
Если вам нравятся микро-оптимизации все, объекты возвращают false
в сообщение .nil?
, кроме самого объекта nil
, а объект с сообщением ==
будет выполнять крошечное микро сравнение
с другим объектом, чтобы определить, является ли он одним и тем же объектом.
* См. комментарии.
Ответ 2
Лично я предпочитаю object.nil?
, поскольку он может быть менее запутанным на более длинных строках; однако я также обычно использую object.blank?
, если я работаю в Rails, так как это также проверяет, пуста ли переменная.
Ответ 3
Синтаксис и стиль в сторону, я хотел видеть, как "одни и те же" различные подходы к тестированию для nil были. Итак, я написал несколько тестов, чтобы увидеть, и бросил на него различные формы тестирования nil.
TL; DR - результаты Первые
Фактические результаты показали, что использование obj
в качестве проверки nil является самым быстрым во всех случаях. obj
последовательно быстрее на 30% или больше, чем проверка obj.nil?
.
Удивительно, что obj
выполняет примерно в 3-4 раза быстрее, чем вариации на obj == nil
, для которых, как представляется, существует штраф за штрафные санкции.
Хотите ускорить свой интенсивный алгоритм на 200% -300%? Преобразуйте все теги obj == null
в obj
. Хотите скомбинировать производительность вашего кода? Используйте obj == null
везде, где можете. (просто шутите: не мешкайте свой код!).
В конечном счете всегда используйте obj
. Это jives с правило Ruby Style: Не делать явных non-nil, если вы не имеете дело с булевыми значениями.
Условия тестирования
ОК, так что это результаты, так как же этот эталонный тест собрался, какие тесты были выполнены и каковы детали результатов?
Проверки nil, с которыми я столкнулся, заключаются в следующем:
- OBJ
- obj.nil?
- ! OBJ
- !! OBJ
- obj == nil
- obj!= 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 показаны здесь (и доступны в 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)
Ответ 4
Во многих случаях не проверяйте значение логической истины
Хотя две операции очень разные, я уверен, что они всегда будут давать одинаковый результат, по крайней мере, пока кто-то на грани чего-то не решит переопределить метод Object #nil?
. (Один вызывает метод #nil?
, унаследованный от объекта или переопределенный в NilClass
, и один сравнивается с синтаксисом nil
.)
Я бы предположил, что, когда вы сомневаетесь, вы идете на третий путь, и просто проверяете истинное значение выражения.
Итак, if x
, а не if x == nil
или if x.nil?
, чтобы иметь этот тест DTRT, когда значение выражения ложно. Работа таким образом также может помочь избежать соблазна кого-то определить FalseClass#nil?
как истинного.
Ответ 5
Вы можете использовать символ # to_proc на nil?
, тогда как это не будет практично на 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
Я вообще не использую .nil?
, когда вы можете сделать:
unless obj
// do work
end
Он работает медленнее, используя .nil?
, но не заметно. .nil?
- это всего лишь метод проверки того, равен ли этот объект нулю, кроме визуальной привлекательности и очень небольшой производительности, которую он принимает, нет разницы.
Ответ 7
Некоторые могут предполагать, что использование .nil? медленнее, чем простое сравнение, что имеет смысл, когда вы думаете об этом.
Но если масштаб и скорость не беспокоят вас, тогда .nil? возможно, более читабельна.