Лучшая рубиновая идиома для «ноля или нуля»
-
03-07-2019 - |
Вопрос
Я ищу краткий способ проверить значение, чтобы узнать, равно ли оно нулю или нулю.В настоящее время я делаю что-то вроде:
if (!val || val == 0)
# Is nil or zero
end
Но это кажется очень неуклюжим.
Решение
Объекты имеют ноль? метод.
if val.nil? || val == 0
[do something]
end
Или, всего лишь для одной инструкции:
[do something] if val.nil? || val == 0
Другие советы
Если вам действительно нравятся имена методов со знаками вопроса в конце:
if val.nil? || val.zero?
# do stuff
end
Ваше решение в порядке, как и некоторые другие решения.
Ruby может заставить вас искать красивый способ сделать все, если вы не будете осторожны.
Во-первых, я думаю, что это самый краткий способ проверить это конкретное условие.
Во-вторых, для меня это запах кода, указывающий на потенциальную ошибку в вашем проекте.Обычно ноль и ноль не должны означать одно и то же.Если возможно, вам следует попытаться исключить возможность того, что val будет равно нулю, прежде чем вы нажмете этот код, либо проверив это в начале метода, либо каким-либо другим механизмом.
У вас может быть совершенно законная причина сделать это, и в этом случае я думаю, что ваш код хорош, но я бы, по крайней мере, подумал о том, чтобы попытаться избавиться от проверки на ноль, если это возможно.
Начиная с Ruby 2.3.0, вы можете комбинировать оператор безопасной навигации (&.
) с Numeric#nonzero?
. &.
возвращает nil
если бы экземпляр был nil
и nonzero?
- если номер был 0
:
unless val&.nonzero?
# Is nil or zero
end
Или постфикс:
do_something unless val&.nonzero?
Вы можете использовать Object.nil?специально проверить ноль (и не попасться между ложью и нулем).Вы также можете добавить метод в Object.
class Object
def nil_or_zero?
return (self.nil? or self == 0)
end
end
my_object = MyClass.new
my_object.nil_or_zero?
==> false
Это не рекомендуется, так как изменения в Object трудно отследить коллегам, и это может сделать ваш код непредсказуемым для других.
Я считаю, что ваш код неверен;на самом деле он будет проверять три значения: nil
, false
, и ноль.Это потому, что !val
выражение истинно для всех значений, которые являются ложными, что в Ruby является nil
и false
.
Лучшее, что я могу сейчас придумать, это
if val == nil || val == 0
# do stuff
end
Что конечно не очень умно, но (очень) понятно.
nil.to_i возвращает ноль, поэтому я часто делаю это:
val.to_i.zero?
Однако вы получите исключение, если val когда-либо будет объектом, который не отвечает_to #to_i.
В моем решении также используются уточнения без условных обозначений.
module Nothingness
refine Numeric do
alias_method :nothing?, :zero?
end
refine NilClass do
alias_method :nothing?, :nil?
end
end
using Nothingness
if val.nothing?
# Do something
end
Rails делает это с помощью методов запроса атрибутов, где помимо false и nil, 0 и "" также оцениваются как false.
if (model.attribute?) # => false if attribute is 0 and model is an ActiveRecord::Base derivation
Однако у него есть своя доля недоброжелателей. http://www.joegrossberg.com/archives/002995.html
Чтобы быть как можно более идиоматическим, я бы предложил это.
if val.nil? or val == 0
# Do something
end
Потому что:
- Он использует ноль?метод.
- Он использует оператор «или», который предпочтительнее ||.
- В нем не используются круглые скобки, которые в данном случае не нужны.Круглые скобки следует использовать только тогда, когда они служат какой-либо цели, например, для отмены приоритета определенных операторов.
Самый короткий и лучший путь должен быть
if val&.>(0)
# do something
end
Для val&.>(0)
он возвращает ноль, когда val равно нулю, поскольку > по сути тоже является методом, nil равен false в Ruby.Он возвращает false, когда val == 0
.
Коротко и ясно
[0, nil].include?(val)
Я имею дело с этим, определяя "есть?" Метод, который я могу затем реализовать по -разному в различных классах.Итак, для массива "есть?" означает «размер> 0»;для Fixnum это означает «self != 0»;для String это означает «self != ''».Нилкласс, конечно, определяет "есть?" как только возвращение нуля.
Вы можете использовать case
если хочешь:
case val with nil, 0
# do stuff
end
Затем вы можете использовать все, что работает с ===
, что иногда приятно.Или сделайте что-то вроде этого:
not_valid = nil, 0
case val1 with *not_valid
# do stuff
end
#do other stuff
case val2 with *not_valid, false #Test for values that is nil, 0 or false
# do other other stuff
end
Это не совсем хороший ООП, но очень гибкий и работает.Мой if
обычно это заканчивается так case
в любом случае.
Конечно Enum.any?
/Enum.include?
тоже вроде работает...если вам нравится быть по-настоящему загадочным:
if [0, nil].include? val
#do stuff
end
Конечно, правильнее всего определить метод или функцию.Или, если вам нужно сделать то же самое со многими значениями, используйте комбинацию этих хороших итераторов.
Мне очень нравится Рельс blank?
метод для такого рода вещей, но он не возвращается true
для 0
.Итак, вы можете добавить свой метод:
def nil_zero?
if respond_to?(:zero?)
zero?
else
!self
end
end
И он проверит, равно ли какое-то значение нулю или 0:
nil.nil_zero?
=> true
0.nil_zero?
=> true
10.nil_zero?
=> false
if val.nil_zero?
#...
end
Вместо того, чтобы обезьяна исправляла класс, вы могли бы использовать уточнения начиная с Ruby 2.1.Уточнения аналогичны исправлениям обезьян;при этом они позволяют вам изменять класс, но модификация ограничена областью, в которой вы хотите его использовать.
Это излишне, если вы хотите выполнить эту проверку один раз, но если вы повторяетесь, это отличная альтернатива обезьяньему исправлению.
module NilOrZero
refine Object do
def nil_or_zero?
nil? or zero?
end
end
end
using NilOrZero
class Car
def initialize(speed: 100)
puts speed.nil_or_zero?
end
end
car = Car.new # false
car = Car.new(speed: nil) # true
car = Car.new(speed: 0) # true
Уточнения были изменены в последнюю минуту, чтобы ограничиться файлом.Таким образом, предыдущие примеры, возможно, показали это, но это не сработает.
class Car
using NilOrZero
end
Это очень кратко:
if (val || 0) == 0
# Is nil, false, or zero.
end
Это работает, пока вы не против лечить false
такой же как nil
.В проектах, над которыми я работал, это различие имеет значение лишь время от времени.Остальное время лично я предпочитаю пропускать .nil?
и иметь немного более короткий код.
[Обновлять:Я больше не пишу такого рода вещи.Это работает, но слишком загадочно.Я пытался исправить свои проступки, изменив те немногие места, где я их совершал.]
Я, кстати, не использовал .zero?
поскольку это вызывает исключение, если val
это, скажем, строка.Но .zero?
было бы хорошо, если бы вы знали, что это не так.
Это оценивается как true для нуля и нуля: nil.to_s.to_d == 0
unless (val || 0).zero?
# do stufff
end
Другое решение:
if val.to_i == 0
# do stuff
end
val ||= 0
if val == 0
# do something here
end