Najlepszy idiom ruby ​​dla „zero lub zero”


83

Szukam zwięzłego sposobu sprawdzenia wartości, aby sprawdzić, czy jest zerowa lub zerowa. Obecnie robię coś takiego:

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

Ale to wydaje się bardzo niezdarne.


1
Co powinno się stać, jeśli val jest równy false?
Andrew Grimm,

Odpowiedzi:


70

Obiekty mają zero? metoda .

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

Lub dla jednej instrukcji:

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

5
uważaj na użycie "or" i "and", przy okazji mają one inny i bardzo niski priorytet w porównaniu do || i &&. Zobacz blog.jayfields.com/2007/08/…, aby uzyskać kilka uwag.
Mike Woodhouse,

Zgadzam się z jakimś plakatem przedstawiającym „zapach” w pytaniu, jednak dla mnie jest to bardziej rubinowa.
Jonke

6
Pierwszeństwo ornie wymaga tutaj żadnych odwołań i będzie czytane całkowicie naturalnie dla doświadczonego Rubyisty. Jedynymi ludźmi, którzy kiedykolwiek zostali ugryzieni przez ||versus, orsą ludzie, którzy nigdy nie zaprogramowali Perla :)
ephemient

1
Jedyny czas, o który musisz się martwić || w stosunku do OR pierwszeństwo ma przypisanie, a nie sprawdzanie logiczne. Jednak niski priorytet słowa może być bardzo przydatny do sprawdzania błędów.
Robert K

3
Myślę, że val && val == 0jest lepiej.
Nakilon,

35

Jeśli naprawdę lubisz nazwy metod ze znakami zapytania na końcu:


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

Twoje rozwiązanie jest w porządku, podobnie jak kilka innych rozwiązań.

Ruby może sprawić, że będziesz szukał ładnego sposobu na zrobienie wszystkiego, jeśli nie będziesz ostrożny.


32

Począwszy od Rubiego 2.3.0, możesz łączyć bezpieczny operator nawigacji ( &.) z Numeric#nonzero?. &.zwraca, niljeśli wystąpienie było nili nonzero?- jeśli liczba to 0:

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

Lub postfix:

do_something unless val&.nonzero?

To była wspaniała nowa i nowoczesna odpowiedź. Jest tam również świetny artykuł o tym, dlaczego warto go używać: The Safe Navigation Operator (&.)
In

1
Bardzo zwięzłe, ale bez komentarza zajęłoby mi chwilę zrozumienie, co unless val&.nonzero?przekłada się na "jeśli val wynosi zero lub zero"
Stefan

1
@Stefan, zgodził się. Sam nie użyłbym tego w kodzie produkcyjnym. Uwzględniłem to jako kompletność. Odwrotność jest jednak doskonale czytelna - do_something if val&.nonzero?(aka, jeśli valnie jest nili nie jest zerem).
ndnenkov

29

Po pierwsze, myślę, że to najbardziej zwięzły sposób na sprawdzenie tego konkretnego stanu.

Po drugie, dla mnie jest to zapach kodu, który wskazuje na potencjalną wadę twojego projektu. Generalnie zero i zero nie powinny oznaczać tego samego. Jeśli to możliwe, powinieneś spróbować wyeliminować możliwość, że val będzie zerowa, zanim trafisz na ten kod, albo sprawdzając to na początku metody, albo przez inny mechanizm.

Możesz mieć całkowicie uzasadniony powód, aby to zrobić, w takim przypadku myślę, że twój kod jest dobry, ale przynajmniej rozważę próbę pozbycia się sprawdzania zerowego, jeśli to możliwe.


4
Bardzo dobrze, jeśli chodzi o zapach kodu! +1 Można rozważyć przyjrzenie się wzorowi NullObject en.wikipedia.org/wiki/Null_Object_pattern
abyx

9

Możesz użyć Object.nil? specjalnie przetestować na zero (i nie dać się złapać między fałszem a zerem). Możesz również łączyć metodę w 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

Nie jest to zalecane, ponieważ zmiany w Object są trudne do śledzenia dla współpracowników i mogą spowodować, że Twój kod będzie nieprzewidywalny dla innych.


Modyfikowałbym raczej klasę Numeric niż Object.
epochwolf

6
epochwolf, jeśli umieścisz to w klasie Numeric, to nil?zawsze zwróci false. nil?zwraca tylko wartość true w NilClass.
Ryan McGeary

Można realizować w sposób Object, NilClassi Numeric. Objectwróci false, NilClasswróci truei Numericwróci zero?.
Stefan

6

nil.to_i zwraca zero, więc często robię to:

val.to_i.zero?

Jednak otrzymasz wyjątek, jeśli val kiedykolwiek będzie obiektem, który nie odpowiada na #to_i.


dla odniesienia: odpowiedź Scotta i to, co @AndrewGrimm powiedział o tym:"5".to_i == 5, but you're more or less right. Clever but doesn't actually work
Paul van Leeuwen

4

Uważam, że twój kod jest nieprawidłowy; to będzie w rzeczywistości badania dla trzech wartości: nil, false, i zero. Dzieje się tak, ponieważ !valwyrażenie jest prawdziwe dla wszystkich wartości, które są fałszywe, czyli w Rubim nilifalse .

Najlepsze, co mogę teraz wymyślić, to

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

Co oczywiście nie jest zbyt sprytne, ale (bardzo) jasne.


prawdopodobnie możesz założyć, że dana zmienna jest liczbową. Jeśli nawet nie wiesz, czy jest to Boolean czy Numeric, w kodzie są większe problemy.
Mike Deck

4

Moje rozwiązanie również korzysta z zawężeń bez warunkowych.

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

Railsy robią to za pomocą metod zapytań o atrybuty, gdzie oprócz wartości false i nil wartości 0 i „” również dają wartość false.

if (model.attribute?) # => false if attribute is 0 and model is an ActiveRecord::Base derivation

Ma jednak swoich krytyków. http://www.joegrossberg.com/archives/002995.html


Link nie działa. Ciekawi mnie metoda #attribute, bo nie mogę znaleźć czegoś takiego.
mrzasa

Jest to coś, co Rails / ActiveRecord dodaje w locie. Jeśli Klient ma pole nazwy, imię? jest automatycznie dodawany, aby sprawdzić, czy nazwa jest pusta czy zerowa. Zobacz api.rubyonrails.org/classes/ActiveRecord/Base.html - wyszukaj sekcję „metody wyszukiwania atrybutów”
Gishu

2

Aby być tak idiomatycznym, jak to tylko możliwe, zasugerowałbym to.

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

Dlatego:

  • Wykorzystuje zero? metoda.
  • Używa operatora „or”, który jest lepszy niż ||.
  • Nie używa nawiasów, które w tym przypadku nie są konieczne. Nawiasy powinny być używane tylko wtedy, gdy służą do jakiegoś celu, na przykład przesłaniają pierwszeństwo niektórych operatorów.

1
Chociaż wiem, że pochodzi to dosłownie 7 lat temu, z tego powodu zagłosowałem w dół
Devon Parsons

2

Krótkie i jasne

[0, nil].include?(val)


2
Można to również odwrócić:val.in? [0,nil]
mahemoff

1
@mahemoff to z pewnością najlepszy idiom!
intmarinoreturn0

@mahemoff lepiej wykorzystać to?, ponieważ jest szybszy niż w? ref: stackoverflow.com/questions/28272550/…
Lakhani Aliraza

Słuszna uwaga, ale uruchomiłem go właśnie teraz na nowoczesnym Rubim i w? był faktycznie szybszy dla pojedynczego wywołania, a dla 1000 wywołań są one dość podobne. Oba i tak działają błyskawicznie, na tyle, że imo wpłynie na kilka aplikacji. gist.github.com/mahemoff/7232648e1ccb9d23e0f4670913400bd8
mahemoff

2

Powinien być najkrótszy i najlepszy sposób

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

Ponieważ val&.>(0) zwraca nil, gdy val jest nil, ponieważ> w zasadzie jest także metodą, nil równe jest fałszowi w ruby. Zwraca fałsz, kiedy val == 0.


3
Najkrótszy co ?, najlepszy sposób niż co?
Sebastian Palma,

0

Radzę sobie z tym, definiując „jest”? metodę, którą mogę następnie wdrożyć w różny sposób na różnych klasach. Więc w przypadku tablicy „jest?” oznacza „rozmiar> 0”; dla Fixnum oznacza „self! = 0”; w przypadku String oznacza to "self! = ''". NilClass oczywiście definiuje „jest?” jako po prostu zwracając zero.


0

Możesz użyć, casejeśli chcesz:

 case val with nil, 0
      # do stuff
 end

Wtedy możesz użyć wszystkiego, co działa ===, co czasami jest miłe. Lub zrób coś takiego:

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

To nie jest do końca dobre OOP, ale jest bardzo elastyczne i działa. Moje ifi tak zwykle kończą się jako cases.

Oczywiście Enum.any?/ Enum.include?rodzaj też działa ... jeśli lubisz być naprawdę tajemniczy:

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

Oczywiście należy zdefiniować metodę lub funkcję. Lub, jeśli musisz zrobić to samo z wieloma wartościami, użyj kombinacji tych ładnych iteratorów.


0

Naprawdę podoba mi się blank?metoda Railsów do tego typu rzeczy, ale nie wróci truedo tego 0. Możesz więc dodać swoją metodę:

def nil_zero? 
  if respond_to?(:zero?) 
    zero? 
  else 
    !self 
  end 
end 

I sprawdzi, czy jakaś wartość jest zerowa czy 0:

nil.nil_zero?
=> true
0.nil_zero?
=> true
10.nil_zero?
=> false

if val.nil_zero?
  #...
end

0

Zamiast małpy łatać klasę, możesz użyć udoskonaleń począwszy od Rubiego 2.1. Udoskonalenia są podobne do poprawiania małp; w związku z tym umożliwiają modyfikowanie klasy, ale modyfikacja jest ograniczona do zakresu, w którym chcesz jej użyć.

To przesada, jeśli chcesz raz to sprawdzić, ale jeśli się powtarzasz, jest to świetna alternatywa dla małpiego łatania.

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

Doprecyzowania zostały zmienione w ostatniej chwili, aby uzyskać zakres pliku. Więc wcześniejsze przykłady mogły to pokazać, co nie zadziała.

class Car
  using NilOrZero
end

0

To jest bardzo zwięzłe:

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

Działa tak długo, jak nie masz nic przeciwko traktowaniu falsetego samego nil. W projektach, nad którymi pracowałem, to rozróżnienie ma znaczenie tylko raz na jakiś czas. Przez resztę czasu osobiście wolę przeskakiwać .nil?i mieć nieco krótszy kod.

[ Aktualizacja : nie piszę już tego typu rzeczy. Działa, ale jest zbyt tajemniczy. Próbowałem naprawić swoje złe uczynki, zmieniając kilka miejsc, w których to zrobiłem.]

Nawiasem mówiąc, nie użyłem, .zero?ponieważ powoduje to wyjątek, jeśli valjest, powiedzmy, ciągiem. Ale .zero?byłoby dobrze, gdybyś wiedział, że tak nie jest.


[edited]Pomyślałem, że jeśli valjest nil, to wyrażenie zwróci wartość nil == 0(i zwróci false). Wygląda jednak na to, że faktycznie działa, chociaż nie sądzę, aby był bardzo czytelny.
omnikron

Teraz uważam, że moje podejście tutaj było zbyt tajemnicze, żeby uratować kilka postaci, a teraz nie napisałbym tego w ten sposób. Nie spodziewam się więc żadnych pozytywnych głosów, ale jestem trochę zaskoczony negatywnymi opiniami za działającą odpowiedź (choć miała wątpliwy gust) ...
antinome

0

To daje wartość true dla zera i zera: nil.to_s.to_d == 0



-1

Inne rozwiązanie:

if val.to_i == 0
  # do stuff
end

1
Działa to tylko wtedy, gdy val jest liczbą całkowitą lub nil; 0.5.to_i == 0, any_string.to_i == 0, etc.
Zach Langley,

"5".to_i == 5, ale masz mniej więcej rację. Sprytne, ale tak naprawdę nie działa.
Andrew Grimm,

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

3
-1: A co, jeśli valtrzeba zachować wartość w ? Tą metodą przebijasz dane wejściowe i istnieją lepsze, mniej destrukcyjne sposoby sprawdzania, czy nie ma zera lub zera.
Platinum Azure

3
A co powiesz na if (val || 0) .zero?
timkay
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.