Beste Rubinsprache für „Null oder Null“


82

Ich suche nach einer präzisen Möglichkeit, einen Wert zu überprüfen, um festzustellen, ob er Null oder Null ist. Zur Zeit mache ich so etwas wie:

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

Aber das scheint sehr ungeschickt.


1
Was soll passieren, wenn val gleich ist false?
Andrew Grimm

Antworten:


70

Objekte haben eine Null? Methode .

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

Oder für nur eine Anweisung:

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

5
Achten Sie auf die Verwendung von "oder" und "und". Übrigens haben sie eine andere und sehr niedrige Priorität als || und &&. Siehe blog.jayfields.com/2007/08/… für einige Anmerkungen.
Mike Woodhouse

Ich stimme einem Plakat des "Geruchs" in der Frage zu, aber für mich ist dies eher der Rubinweg.
Jonke

6
Der Vorrang von orerfordert hier keine Parens und wird einem erfahrenen Rubyisten ganz natürlich vorlesen. Die einzigen Menschen , die jemals gebissen werden durch ||Vergleich orsind Menschen , die nie Perl programmiert haben :)
ephemient

1
Das einzige Mal, dass Sie sich Sorgen machen müssen || versus ODER-Priorität hat bei der Zuweisung keine boolesche Prüfung. Die niedrige Priorität des Wortes kann jedoch für die Fehlerprüfung sehr nützlich sein.
Robert K

3
Ich denke val && val == 0ist besser.
Nakilon

35

Wenn Sie Methodennamen mit Fragezeichen am Ende wirklich mögen:


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

Ihre Lösung ist in Ordnung, ebenso wie einige der anderen Lösungen.

Ruby kann Sie dazu bringen, nach einem hübschen Weg zu suchen, um alles zu tun, wenn Sie nicht vorsichtig sind.


31

Ab Ruby 2.3.0 können Sie den sicheren Navigationsoperator ( &.) mit kombinieren Numeric#nonzero?. &.Gibt zurück, nilwenn die Instanz war nilund nonzero?- wenn die Nummer war 0:

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

Oder Postfix:

do_something unless val&.nonzero?

Dies war eine großartige neue und moderne Antwort. Es gibt auch einen großartigen Artikel darüber, warum es hier verwendet wird: Der sichere Navigationsoperator (&.) In Ruby
Michael Gaskill

1
Sehr prägnant, aber ohne den Kommentar würde ich einen Moment brauchen, unless val&.nonzero?um zu verstehen, was übersetzt "wenn val null oder null ist" bedeutet
Stefan

1
@Stefan, stimmte zu. Ich würde dies nicht selbst im Produktionscode verwenden. Ich habe es für den Vervollständigungismus aufgenommen. Das Gegenteil ist jedoch perfekt lesbar - do_something if val&.nonzero?(auch bekannt als if valis not nilund not zero).
ndnenkov

29

Zunächst einmal denke ich, dass dies die präziseste Art ist, wie Sie nach dieser bestimmten Bedingung suchen können.

Zweitens ist dies für mich ein Code-Geruch, der auf einen möglichen Fehler in Ihrem Design hinweist. Im Allgemeinen sollten Null und Null nicht dasselbe bedeuten. Wenn möglich, sollten Sie versuchen, die Möglichkeit auszuschließen, dass val Null ist, bevor Sie diesen Code drücken, indem Sie dies entweder zu Beginn der Methode oder durch einen anderen Mechanismus überprüfen.

Möglicherweise haben Sie einen absolut legitimen Grund, dies zu tun. In diesem Fall halte ich Ihren Code für gut, aber ich würde zumindest in Betracht ziehen, den Null-Check nach Möglichkeit loszuwerden.


4
Sehr richtig über den Code-Geruch! 1 könnte ein Blick in NullObject Muster betrachten en.wikipedia.org/wiki/Null_Object_pattern
abyx

9

Sie können die Object.nil? spezifisch auf Null zu testen (und nicht zwischen falsch und Null gefangen zu werden). Sie können eine Methode auch in Object patchen.

class Object
   def nil_or_zero?
     return (self.nil? or self == 0)
   end
end

my_object = MyClass.new
my_object.nil_or_zero?
==> false

Dies wird nicht empfohlen, da Änderungen an Object für Mitarbeiter schwer nachzuvollziehen sind und Ihr Code für andere möglicherweise unvorhersehbar ist.


Ich würde die numerische Klasse anstelle von Object ändern.
Epochwolf

6
epochwolf, wenn Sie dies auf die numerische Klasse setzen, dann nil?würde immer false zurückgeben. nil?Gibt nur in der NilClass true zurück.
Ryan McGeary

Sie könnten das Verfahren in implementieren Object, NilClassund Numeric. Objectwürde zurückkehren false, NilClasswürde zurückkehren trueund Numericwürde zurückkehren zero?.
Stefan

6

nil.to_i gibt Null zurück, deshalb mache ich das oft:

val.to_i.zero?

Sie erhalten jedoch eine Ausnahme, wenn val jemals ein Objekt ist, das nicht auf #to_i reagiert.


als Referenz: Scotts Antwort und was @AndrewGrimm dazu sagte:"5".to_i == 5, but you're more or less right. Clever but doesn't actually work
Paul van Leeuwen

4

Ich glaube, Ihr Code ist falsch. es wird für drei Werte in der Tat Test: nil, falseund null. Dies liegt daran, dass der !valAusdruck für alle Werte wahr ist, die falsch sind, was in Ruby nilund ist false.

Das Beste, was ich mir gerade einfallen lassen kann, ist

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

Was natürlich nicht sehr klug ist, aber (sehr) klar.


Sie können wahrscheinlich davon ausgehen, dass es sich bei der fraglichen Variablen um eine numerische Variable handelt. Wenn Sie nicht einmal wissen, ob es sich um einen Booleschen oder einen Numerischen Wert handelt, gibt es größere Probleme im Code.
Mike Deck

Ich würde .nil verwenden? not == nil
RichH

4

Meine Lösung verwendet auch Verfeinerungen abzüglich der Bedingungen.

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 tut dies über Attributabfragemethoden, wobei neben false und nil auch 0 und "" als false ausgewertet werden.

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

Es hat jedoch seinen Anteil an Kritikern. http://www.joegrossberg.com/archives/002995.html


Link funktioniert nicht. Ich bin neugierig auf die Methode #attribute, weil ich so etwas nicht finden kann.
Mrzasa

Es ist etwas, das Rails / ActiveRecord im laufenden Betrieb hinzufügt. Wenn der Kunde ein Namensfeld hat, Name? wird automatisch hinzugefügt, um zu überprüfen, ob der Name leer oder null ist. Siehe api.rubyonrails.org/classes/ActiveRecord/Base.html - Suche nach Abschnitt 'Attributabfragemethoden'
Gishu

2

Um so idiomatisch wie möglich zu sein, würde ich dies vorschlagen.

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

Weil:

  • Es benutzt die Null? Methode.
  • Es wird der Operator "oder" verwendet, der || vorzuziehen ist.
  • Es werden keine Klammern verwendet, die in diesem Fall nicht erforderlich sind. Klammern sollten nur verwendet werden, wenn sie einem bestimmten Zweck dienen, z. B. dem Überschreiben der Priorität bestimmter Operatoren.

1
Obwohl ich weiß, dass dies buchstäblich von vor 7 Jahren stammt, habe ich aus diesem Grund abgestimmt: github.com/bbatsov/ruby-style-guide#no-and-or-or
Devon Parsons

2

Kurz und klar

[0, nil].include?(val)


2
Kann das auch umkehren:val.in? [0,nil]
Mahemoff

1
@ Mahemoff das ist sicherlich die beste Redewendung dafür!
intmarinoreturn0

@ Mahemoff besser nutzen gehören?, da es schneller als in? ref: stackoverflow.com/questions/28272550/…
Lakhani Aliraza

Guter Punkt, aber ich habe es gerade auf modernem Ruby und in ausgeführt? war tatsächlich schneller für einen einzelnen Aufruf, und für 1000 Aufrufe sind sie ziemlich ähnlich. Beide sind sowieso blitzschnell genug, so dass nur wenige Anwendungen imo betroffen wären. gist.github.com/mahemoff/7232648e1ccb9d23e0f4670913400bd8
Mahemoff

2

Der kürzeste und beste Weg sollte sein

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

Denn val&.>(0) es gibt nil zurück, wenn val nil ist, da> im Grunde auch eine Methode ist, nil gleich false in ruby. Es gibt false zurück, wenn val == 0.


3
Am kürzesten über was?, Bester Weg als was?
Sebastian Palma

0

Ich gehe damit um, indem ich ein "ist?" Methode, die ich dann auf verschiedenen Klassen unterschiedlich implementieren kann. Also für Array "ist?" bedeutet "Größe> 0"; für Fixnum bedeutet es "self! = 0"; für String bedeutet es "self! = ''". NilClass definiert natürlich "ist?" als nur Null zurück.


0

Sie können verwenden, casewenn Sie möchten:

 case val with nil, 0
      # do stuff
 end

Dann können Sie alles verwenden, was funktioniert ===, was manchmal schön ist. Oder machen Sie so etwas:

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

Es ist nicht gerade gut OOP, aber es ist sehr flexibel und es funktioniert. Mein ifs endet normalerweise casesowieso als s.

Natürlich Enum.any?/ Enum.include?Art funktioniert auch ... wenn Sie wirklich kryptisch werden möchten:

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

Das Richtige ist natürlich, eine Methode oder Funktion zu definieren. Wenn Sie dasselbe mit vielen Werten tun müssen, verwenden Sie eine Kombination dieser netten Iteratoren.


0

Ich mag Rails blank?Methode für diese Art von Dingen, aber es wird nicht wieder truefür 0. So können Sie Ihre Methode hinzufügen:

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

Und es wird geprüft, ob ein Wert Null oder 0 ist:

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

if val.nil_zero?
  #...
end

0

Anstatt eine Klasse mit Affen zu patchen, können Sie ab Ruby 2.1 Verfeinerungen verwenden . Verfeinerungen ähneln dem Patchen von Affen. Auf diese Weise können Sie die Klasse ändern, die Änderung ist jedoch auf den Bereich beschränkt, in dem Sie sie verwenden möchten.

Dies ist übertrieben, wenn Sie diese Überprüfung einmal durchführen möchten, aber wenn Sie sich wiederholen, ist dies eine großartige Alternative zum Affen-Patching.

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

Die Verfeinerungen wurden in letzter Minute geändert , um sie in die Datei aufzunehmen. Frühere Beispiele haben dies möglicherweise gezeigt, was jedoch nicht funktioniert.

class Car
  using NilOrZero
end

0

Das ist sehr prägnant:

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

Es funktioniert so lange, wie es Ihnen nichts ausmacht, falsedasselbe zu behandeln wie nil. In den Projekten, an denen ich gearbeitet habe, ist diese Unterscheidung nur gelegentlich von Bedeutung. Den Rest der Zeit überspringe ich persönlich lieber .nil?und habe etwas kürzeren Code.

[ Update : Ich schreibe so etwas nicht mehr. Es funktioniert, ist aber zu kryptisch. Ich habe versucht, meine Missetaten zu korrigieren, indem ich die wenigen Stellen geändert habe, an denen ich es getan habe.]

Übrigens habe ich nicht verwendet, .zero?da dies eine Ausnahme auslöst val, wenn es sich beispielsweise um eine Zeichenfolge handelt. Aber .zero?wäre in Ordnung, wenn Sie wissen, dass dies nicht der Fall ist.


[edited]Ich dachte, wenn valNull wäre, würde dieser Ausdruck zu nil == 0(und damit zurückkehren false) führen. Es scheint jedoch, dass es tatsächlich funktioniert, obwohl ich es nicht für sehr lesbar halte.
Omnikron

Ich glaube jetzt, dass mein Ansatz hier zu kryptisch war, nur um ein paar Zeichen zu speichern, und ich würde es heutzutage nicht so schreiben. Ich erwarte also keine positiven Stimmen, aber ich bin ein wenig überrascht über die negativen Stimmen für eine funktionierende Antwort (obwohl sie von zweifelhaftem Geschmack war) ...
Antinom

0

Dies ergibt für null und null den Wert true: nil.to_s.to_d == 0



-1

Eine andere Lösung:

if val.to_i == 0
  # do stuff
end

1
Dies funktioniert nur, wenn val entweder eine Ganzzahl oder Null ist. 0.5.to_i == 0, any_string.to_i == 0 usw.
Zach Langley

"5".to_i == 5, aber du hast mehr oder weniger recht. Clever, funktioniert aber nicht.
Andrew Grimm

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

3
-1: Was ist, wenn der Wert in valbeibehalten werden muss? Mit dieser Methode werden die Eingabedaten überlastet, und es gibt bessere, weniger zerstörerische Möglichkeiten, um auf Null oder Null zu prüfen.
Platinum Azure

3
Wie wäre es mit if (val || 0) .zero?
Timkay
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.