Лучшая рубиновая идиома для «ноль или ноль»


83

Я ищу краткий способ проверить значение, чтобы узнать, равно ли оно нулю или нулю. Сейчас я делаю что-то вроде:

if (!val || val == 0)
  # Is nil or zero
end

Но это кажется очень неуклюжим.


1
Что должно произойти, если val равно false?
Эндрю Гримм,

Ответы:


70

У объектов ноль? метод .

if val.nil? || val == 0
  [do something]
end

Или всего по одной инструкции:

[do something] if val.nil? || val == 0

5
остерегайтесь использования «или» и «и», кстати, они имеют другой и очень низкий приоритет по сравнению с || и &&. См. Несколько замечаний на сайте blog.jayfields.com/2007/08/… .
Майк Вудхаус,

Я согласен с некоторыми представителями "запаха" в вопросе, но для меня это больше рубиновый путь.
Jonke

6
Приоритет orне требует здесь скобок и будет совершенно естественно прочитан опытным рубистом. Единственные люди, которых когда-либо укусили ||против, or- это люди, которые никогда не программировали Perl :)
ephemient

1
Единственный раз, когда тебе нужно беспокоиться || по сравнению с приоритетом OR - это присваивание, а не логическая проверка. Однако низкий приоритет этого слова может быть очень удобен для проверки ошибок.
Роберт К.

3
Думаю val && val == 0лучше.
Nakilon

35

Если вам действительно нравятся названия методов с вопросительными знаками в конце:


if val.nil? || val.zero?
  # do stuff
end

Ваше решение в порядке, как и некоторые другие решения.

Ruby может заставить вас искать красивый способ сделать все, если вы не будете осторожны.


32

Начиная с Ruby 2.3.0, вы можете комбинировать безопасный оператор навигации ( &.) с Numeric#nonzero?. &.возвращает, nilесли экземпляр был nilи nonzero?- если номер был 0:

unless val&.nonzero?
  # Is nil or zero
end

Или постфикс:

do_something unless val&.nonzero?

Это был потрясающий новый и современный ответ. Кроме того, есть отличная статья о том, зачем его использовать здесь: Оператор безопасной навигации (&.) В Ruby
Майкл Гаскилл,

1
Очень кратко, но без комментария мне потребовалось бы время, чтобы понять, что unless val&.nonzero?переводится как «если val равно нулю или нулю»
Стефан

1
@ Стефан, согласился. Я бы сам не стал использовать это в производственном коде. Я включил его для завершения. Хотя обратное отлично читается - do_something if val&.nonzero?(иначе, если valнет nilи не ноль).
ndnenkov

29

Во-первых, я думаю, что это наиболее краткий способ проверить это конкретное состояние.

Во-вторых, для меня это запах кода, который указывает на потенциальную ошибку в вашем дизайне. Обычно ноль и ноль не должны означать одно и то же. Если возможно, вам следует попытаться исключить возможность того, что val будет равно нулю, прежде чем вы нажмете этот код, либо проверив это в начале метода, либо каким-либо другим механизмом.

У вас может быть вполне законная причина для этого, и в этом случае я думаю, что ваш код хорош, но я бы по крайней мере подумал о том, чтобы попытаться избавиться от проверки nil, если это возможно.


4
Совершенно верно насчет запаха кода! +1 Можно было бы рассмотреть шаблон NullObject en.wikipedia.org/wiki/Null_Object_pattern
abyx

9

Вы можете использовать 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, и это может сделать ваш код непредсказуемым для других.


Я бы изменил класс Numeric, а не Object.
epochwolf

6
epochwolf, если вы поместите это в класс Numeric, то nil?всегда вернет false. nil?возвращает true только для NilClass.
Райан МакГири,

Вы можете реализовать этот метод в Object, NilClassи Numeric. Objectвернется false, NilClassвернется trueи Numericвернется zero?.
Стефан

6

nil.to_i возвращает ноль, поэтому я часто делаю это:

val.to_i.zero?

Однако вы получите исключение, если val когда-либо является объектом, который не отвечает_to #to_i.


для справки: ответ Скотта и то, что по этому поводу сказал @AndrewGrimm:"5".to_i == 5, but you're more or less right. Clever but doesn't actually work
Пол ван

4

Я считаю, что ваш код неверен; это будет на самом деле тест для трех значений: nil, falseи ноль. Это потому, что !valвыражение истинно для всех ложных значений, которыми в Ruby является nilи false.

Лучшее, что я могу придумать прямо сейчас, это

if val == nil || val == 0
  # do stuff
end

Что конечно не очень умно, но (очень) понятно.


вы, вероятно, можете предположить, что рассматриваемая переменная является числовой. Если вы даже не знаете, логическое это или числовое значение, в коде есть более серьезные проблемы.
Майк Дек

4

В моем решении также используются уточнения без условных выражений.

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

2

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


Ссылка не работает. Мне любопытен метод #attribute, потому что я не могу найти ничего подобного.
mrzasa

Это то, что Rails / ActiveRecord добавляет на лету. Если у клиента есть поле имени, имя? автоматически добавляется, чтобы проверить, является ли имя пустым или нулевым. См. Api.rubyonrails.org/classes/ActiveRecord/Base.html - поиск раздела «методы запроса атрибутов»
Гишу,

2

Чтобы быть как можно более идиоматичным, я предлагаю следующее.

if val.nil? or val == 0
    # Do something
end

Потому как:

  • Он использует ноль? метод.
  • Он использует оператор "или", который предпочтительнее ||.
  • В нем не используются круглые скобки, в которых нет необходимости. Круглые скобки следует использовать только тогда, когда они служат какой-либо цели, например, для отмены приоритета определенных операторов.

1
Хотя я знаю, что это было буквально 7 лет назад, я проголосовал против из-за этого: github.com/bbatsov/ruby-style-guide#no-and-or-or
Devon Parsons

2

Коротко и ясно

[0, nil].include?(val)


2
Также можно изменить это:val.in? [0,nil]
mahemoff

1
@mahemoff, это определенно лучшая идиома для этого!
intmarinoreturn0

@mahemoff лучше использовать include ?, так как он быстрее, чем в? ссылка: stackoverflow.com/questions/28272550/…
Лахани Алираза

Хорошее замечание, но я только что запустил его на современном Ruby и в? был на самом деле быстрее для одного вызова, а для 1000 вызовов они очень похожи. В любом случае, оба они молниеносно быстры, и я думаю, это повлияет на несколько приложений. gist.github.com/mahemoff/7232648e1ccb9d23e0f4670913400bd8
mahemoff 06

2

Самый короткий и лучший способ должен быть

if val&.>(0)
  # do something
end

Ибо val&.>(0) он возвращает nil, когда val равно nil, поскольку> в основном также является методом, nil равно false в ruby. Он возвращает false, когда val == 0.


3
Кратчайший по чему? Лучший способ, чем что?
Себастьян Пальма

0

Я имею дело с этим, определяя "есть?" метод, который я затем могу реализовать по-разному для разных классов. Итак, для массива "есть?" означает «размер> 0»; для Fixnum это означает «self! = 0»; для String это означает «self! = ''». NilClass, конечно же, определяет "есть?" как просто возвращение ноль.


0

Вы можете использовать, 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

Это не совсем хорошее ООП, но оно очень гибкое и работает. Мои ifs в caseлюбом случае обычно заканчиваются как s.

Конечно Enum.any?/ Enum.include?вид тоже работает ... если вам нравится быть действительно загадочным:

if [0, nil].include? val
    #do stuff
end

Правильно, конечно, определить метод или функцию. Или, если вам нужно проделать то же самое со многими значениями, используйте комбинацию этих хороших итераторов.


0

Я на самом деле , как Rails 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

0

Вместо того, чтобы обезьяны исправлять класс, вы можете использовать улучшения, начиная с 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

0

Это очень кратко:

if (val || 0) == 0
  # Is nil, false, or zero.
end

Это работает до тех пор, пока вы не против обращаться falseс тем же, что и nil. В проектах, над которыми я работал, это различие имеет значение лишь изредка. В остальное время я предпочитаю пропускать .nil?и иметь немного более короткий код.

[ Обновление : я больше не пишу такие вещи. Это работает, но слишком загадочно. Я пытался исправить свои проступки, изменив несколько мест, где я это сделал.]

Кстати, я не использовал, .zero?так как это вызывает исключение, если val, скажем, строка. Но .zero?было бы хорошо, если бы вы знали, что это не так.


[edited]Я думал, что если бы valбыло nil, то это выражение оценило бы nil == 0(и, таким образом, вернет false). Однако, похоже, он действительно работает, хотя я не думаю, что он очень удобочитаемый.
omnikron

Теперь я считаю, что мой подход здесь был слишком загадочным, просто чтобы сохранить несколько символов, и я бы не стал писать это так сейчас. Так что я не ожидаю каких-либо положительных отзывов, но я немного удивлен тем, что отрицательные голоса за рабочий ответ (хотя у него были сомнительные вкусы) ...
antinome 01



-1

Другое решение:

if val.to_i == 0
  # do stuff
end

1
Это работает, только если val является целым или нулевым числом; 0.5.to_i == 0, any_string.to_i == 0 и т. Д.
Зак Лэнгли,

"5".to_i == 5, но ты более-менее прав. Умно, но на самом деле не работает.
Эндрю Гримм,

-3
val ||= 0
if val == 0
# do something here
end

3
-1: Что делать, если значение valнужно сохранить? Вы сбиваете входные данные с помощью этого метода, и есть более эффективные и менее разрушительные способы проверки на ноль или ноль.
Platinum Azure

3
Как насчет if (val || 0) .zero?
timkay
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.