Найкраща рубінова ідіома для "нуль чи нуль"


83

Я шукаю стислий спосіб перевірити значення на нуль чи нуль. В даний час я роблю щось на зразок:

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

Але це здається дуже незграбним.


1
Що має статися, якщо val дорівнює false?
Andrew Grimm,

Відповіді:


70

Об'єкти мають нуль? метод .

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

Або лише для однієї інструкції:

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

5
слідкуйте за використанням "або" та "та", до речі, вони мають різний і дуже низький пріоритет у порівнянні з || та &&. Деякі зауваження див. У blog.jayfields.com/2007/08/… .
Mike Woodhouse

Я погоджуюсь з деяким плакатом "запаху" у питанні, проте для мене це більше рубіновий спосіб.
Йонке,

6
Пріоритет orне вимагає яких - або дужок тут, і буде читати повністю природно дослідне Rubyist. Єдині люди , які коли - або укушені ||проти orлюди , які ніколи не запрограмовані Perl :)
ephemient

1
Єдиний раз, коли вам потрібно турбуватися про || проти АБО пріоритет - у присвоєнні, а не булевій перевірці. Однак низький пріоритет слова може бути дуже зручним для перевірки помилок.
Роберт К

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?

Це була приголомшлива нова і сучасна відповідь. Крім того, тут є чудова стаття про те, навіщо її використовувати: Оператор безпечної навігації (&.) У Рубі
Майкл Гаскілл

1
Дуже лаконічно, але без коментаря мені знадобиться хвилинка, щоб зрозуміти, що unless val&.nonzero?перекладається як "якщо вал рівний нулю чи нулю"
Стефан

1
@Stefan, погодився. Я б не використовував це у виробничому коді сам. Я включив його для завершення. Навпаки, проте, цілком читається - do_something if val&.nonzero?(інакше, якщо valнемає nilі не нуль).
ndnenkov

29

По-перше, я думаю, що це найкоротший спосіб перевірити наявність певного стану.

По-друге, для мене це запах коду, який вказує на потенційний недолік у вашому дизайні. Взагалі нуль і нуль не повинні означати одне і те ж. Якщо це можливо, вам слід спробувати усунути ймовірність того, що val буде нульовим, перш ніж натиснути цей код, або перевіривши це на початку методу, або іншим механізмом.

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


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, якщо ви покладете це на числовий клас, то nil?завжди буде повертати false. nil?повертає true лише для NilClass.
Ryan McGeary

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

6

nil.to_i повертає нуль, тому я часто роблю це:

val.to_i.zero?

Однак ви отримаєте виняток, якщо val коли-небудь є об'єктом, який не реагує_на #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

Що, звичайно, не дуже розумно, але (дуже) зрозуміло.


ви, мабуть, можете припустити, що змінна, про яку йде мова, є числовою. Якщо ви навіть не знаєте, логічний це чи числовий, у коді є більші проблеми.
Mike Deck,

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 років тому, я через це проголосував
Девон Парсонс

2

Короткий і зрозумілий

[0, nil].include?(val)


2
Можна також змінити це:val.in? [0,nil]
mahemoff

1
@mahemoff, що, безумовно, найкраща ідіома для цього!
intmarinoreturn0

@mahemoff краще використовувати include?, оскільки це швидше, ніж у? посилання: stackoverflow.com/questions/28272550 / ...
Lakhani Aliraza

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

2

Повинен бути найкоротший і найкращий спосіб

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

Бо val&.>(0) він повертає nil, коли val дорівнює nil, оскільки> в основному також є методом, nil рівним false в ruby. Повертає false, коли val == 0.


3
Найкоротший за що ?, найкращий спосіб, ніж що?
Себастьян Пальма,

0

Я маю справу з цим, визначаючи "це?" метод, який я можу потім реалізувати по-різному в різних класах. Тож для Array "є?" означає "розмір> 0"; для Fixnum це означає "себе! = 0"; для String це означає "себе! = ''". 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 == 0(і, отже, повертати false). Однак, схоже, це насправді працює, хоча я не думаю, що це дуже читабельно.
omnikron

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

0

Це вважає істиною для нуля та нуля: nil.to_s.to_d == 0



-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, але ви більш-менш праві. Розумний, але насправді не працює.
Andrew Grimm,

-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.