Mejor modismo rubí para "nil or zero"

82

Estoy buscando una forma concisa de verificar un valor para ver si es nulo o cero. Actualmente estoy haciendo algo como:

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

Pero esto parece muy torpe.

csexton
fuente
1
¿Qué debería pasar si val es igual a false?
Andrew Grimm

Respuestas:

70

¿Los objetos tienen un cero? método .

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

O, para una sola instrucción:

[do something] if val.nil? || val == 0
Christian Lescuyer
fuente
5
cuidado con el uso de "o" y "y", por cierto, tienen una precedencia diferente y muy baja en comparación con || y &&. Consulte blog.jayfields.com/2007/08/… para ver algunos comentarios.
Mike Woodhouse
Estoy de acuerdo con algún cartel del "olor" en la pregunta, sin embargo, para mí esto es más el rubí.
Jonke
6
La precedencia de orno requiere ningún parens aquí, y se leerá de forma completamente natural para un rubyista experimentado. Las únicas personas que alguna vez son mordidas por ||versus orson las personas que nunca han programado Perl :)
ephemient
1
El único momento por el que debes preocuparte || versus la precedencia OR está en la asignación, no en la verificación booleana. Sin embargo, la baja precedencia de la palabra puede ser muy útil para la verificación de errores.
Robert K
3
Creo que val && val == 0es mejor.
Nakilon
35

Si realmente te gustan los nombres de métodos con signos de interrogación al final:


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

Su solución está bien, al igual que algunas de las otras soluciones.

Ruby puede hacerte buscar una manera bonita de hacer todo, si no tienes cuidado.


fuente
31

Desde Ruby 2.3.0 en adelante, puede combinar el operador de navegación segura ( &.) con Numeric#nonzero?. &.devuelve nilsi la instancia fue nily nonzero?- si el número fue 0:

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

O sufijo:

do_something unless val&.nonzero?
ndnenkov
fuente
Esta fue una excelente respuesta nueva y moderna. Además, hay un gran artículo sobre por qué usarlo aquí: El operador de navegación segura (&.) En Ruby
Michael Gaskill
1
Muy conciso, pero sin el comentario, me tomaría un momento entender que se unless val&.nonzero?traduce como "si val es nulo o cero"
Stefan
1
@Stefan, estuvo de acuerdo. Yo mismo no usaría esto en el código de producción. Lo incluí por completismo. Sin embargo, lo opuesto es perfectamente legible do_something if val&.nonzero?(también conocido como si valno es nily no es cero).
ndnenkov
29

En primer lugar, creo que esa es la forma más concisa de verificar esa condición en particular.

En segundo lugar, para mí, este es un olor a código que indica una falla potencial en su diseño. Generalmente, cero y cero no deberían significar lo mismo. Si es posible, debe intentar eliminar la posibilidad de que val sea nulo antes de acceder a este código, ya sea verificándolo al principio del método o mediante algún otro mecanismo.

Es posible que tenga una razón perfectamente legítima para hacer esto, en cuyo caso creo que su código es bueno, pero al menos consideraría intentar deshacerse de la verificación nula si es posible.

Mike Deck
fuente
4
¡Muy bien sobre el olor del código! +1 Podría considerar buscar en el patrón NullObject en.wikipedia.org/wiki/Null_Object_pattern
abyx
9

¿Puede utilizar Object.nil? para probar nil específicamente (y no quedar atrapado entre falso y nil). También puede parchear un método en 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

Esto no se recomienda ya que los cambios en Object son difíciles de rastrear para los compañeros de trabajo y pueden hacer que su código sea impredecible para otros.

Adrian Dunston
fuente
Modificaría la clase Numeric en lugar de Object.
epochwolf
6
epochwolf, si pones esto en la clase Numeric, nil?siempre devolverá falso. nil?solo devuelve verdadero en NilClass.
Ryan McGeary
Se podría aplicar el método en Object, NilClassy Numeric. Objectvolvería false, NilClassvolvería truey Numericvolvería zero?.
Stefan
6

nil.to_i devuelve cero, así que a menudo hago esto:

val.to_i.zero?

Sin embargo, obtendrá una excepción si val alguna vez es un objeto que no responde_to #to_i.

ntl
fuente
para referencia: la respuesta de Scott y lo que @AndrewGrimm dijo al respecto:"5".to_i == 5, but you're more or less right. Clever but doesn't actually work
Paul van Leeuwen
4

Creo que tu código es incorrecto; lo hará en la prueba de hecho para tres valores: nil, falsey cero. Esto se debe a que la !valexpresión es verdadera para todos los valores que son falsos, que en Ruby es nily false.

Lo mejor que se me ocurre ahora mismo es

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

Lo cual, por supuesto, no es muy inteligente, pero (muy) claro.

relajarse
fuente
probablemente pueda asumir que la variable en cuestión es numérica. Si ni siquiera sabe si es booleano o numérico, hay problemas mayores en el código.
Mike Deck
¿Usaría .nil? not == nil
RichH
4

Mi solución también usa Refinamientos, menos los condicionales.

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
RichOrElse
fuente
2

Rails hace esto a través de métodos de consulta de atributos, donde además de falso y nulo, 0 y "" también se evalúan como falso.

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

Sin embargo, tiene su parte de detractores. http://www.joegrossberg.com/archives/002995.html

Gishu
fuente
El enlace no funciona. Tengo curiosidad por el método #attribute, porque no puedo encontrar nada de eso.
mrzasa
Es algo que Rails / ActiveRecord agrega sobre la marcha. Si el cliente tiene un campo de nombre, ¿nombre? se agrega automáticamente para verificar si el nombre está en blanco o es nulo. Consulte api.rubyonrails.org/classes/ActiveRecord/Base.html - busque la sección 'métodos de consulta de atributos'
Gishu
2

Para ser lo más idiomático posible, sugiero esto.

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

Porque:

  • ¿Utiliza el nulo? método.
  • Utiliza el operador "o", que es preferible a ||.
  • No utiliza paréntesis, que no son necesarios en este caso. Los paréntesis solo deben usarse cuando tengan algún propósito, como anular la precedencia de ciertos operadores.
Joshua Swink
fuente
1
Aunque sé que esto es literalmente de hace 7 años, voté en contra debido a esto: github.com/bbatsov/ruby-style-guide#no-and-or-or
Devon Parsons
2

Corto y claro

[0, nil].include?(val)

Stanislav Kr.
fuente
2
También puede revertir eso:val.in? [0,nil]
mahemoff
1
@mahemoff ¡ese es ciertamente el mejor idioma para eso!
intmarinoreturn0
@mahemoff mejor uso incluir?, ya que es más rápido que en? ref: stackoverflow.com/questions/28272550/…
Lakhani Aliraza
Buen punto, pero acabo de ejecutarlo en Ruby moderno y en? en realidad fue más rápido para una sola invocación, y para 1000 invocaciones, son bastante similares. Ambos son increíblemente rápidos de todos modos, lo suficiente como para que pocas aplicaciones se vean afectadas en mi opinión. gist.github.com/mahemoff/7232648e1ccb9d23e0f4670913400bd8
mahemoff
2

El camino más corto y mejor debe ser

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

Porque val&.>(0) devuelve nil cuando val es nil ya que> básicamente también es un método, nil es igual a falso en ruby. Devuelve falso cuando val == 0.

usuario2097847
fuente
3
¿Más corto sobre qué ?, ¿mejor manera que qué?
Sebastian Palma
0

Me ocupo de esto definiendo un "¿es?" método, que luego puedo implementar de manera diferente en varias clases. Entonces, para Array, "es?" significa "tamaño> 0"; para Fixnum significa "self! = 0"; para String significa "self! = ''". NilClass, por supuesto, define "es?" como devolver nada.

glenn mcdonald
fuente
0

Puede usar casesi lo desea:

 case val with nil, 0
      # do stuff
 end

Entonces puedes usar cualquier cosa que funcione ===, lo cual a veces es bueno. O haz algo como esto:

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

No es exactamente una buena OOP, pero es muy flexible y funciona. Mis ifs suelen terminar comocase todos modos, s.

Por supuesto Enum.any?/ Enum.include?también funciona ... si te gusta ser realmente críptico:

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

Por supuesto, lo correcto es definir un método o función. O, si tiene que hacer lo mismo con muchos valores, use una combinación de esos buenos iteradores.


fuente
0

Me gusta mucho los carriles blank?no método para ese tipo de cosas, pero va a volver truea 0. Entonces puedes agregar tu método:

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

Y comprobará si algún valor es nulo o 0:

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

if val.nil_zero?
  #...
end
klew
fuente
0

En lugar de mono parchear una clase, puede usar mejoras a partir de Ruby 2.1. Los refinamientos son similares al parche de mono; en eso, le permiten modificar la clase, pero la modificación se limita al alcance en el que desea usarla.

Esto es excesivo si desea hacer esta verificación una vez, pero si lo repite, es una excelente alternativa a los parches de monos.

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

Los refinamientos se cambiaron en el último minuto para ajustarse al alcance del archivo. Así que los ejemplos anteriores pueden haber demostrado esto, lo que no funcionará.

class Car
  using NilOrZero
end
Mohamad
fuente
0

Esto es muy conciso:

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

Funciona siempre que no le importe tratar falselo mismo que nil. En los proyectos en los que he trabajado, esa distinción solo importa de vez en cuando. El resto del tiempo, personalmente prefiero omitir .nil?y tener un código un poco más corto.

[ Actualización : ya no escribo este tipo de cosas. Funciona pero es demasiado críptico. He tratado de corregir mis fechorías cambiando los pocos lugares donde lo hice].

Por cierto, no lo usé .zero?ya que esto genera una excepción si vales, digamos, una cadena. Pero .zero?estaría bien si sabe que ese no es el caso.

antinoma
fuente
[edited]Pensé que si valera nulo, esta expresión se evaluaría nil == 0(y, por lo tanto, regresaría false). Sin embargo, parece que funciona, aunque no creo que sea muy legible.
omnikron
Ahora creo que mi enfoque aquí fue demasiado críptico, solo para salvar algunos caracteres, y no lo escribiría así hoy en día. Así que no espero ningún voto positivo, pero estoy un poco sorprendido por los votos negativos para una respuesta funcional (aunque fue de dudoso gusto) ...
antinome
0

Esto se evalúa como verdadero para nil y cero: nil.to_s.to_d == 0

Sam
fuente
0
unless (val || 0).zero?

    # do stufff

end
Abel
fuente
-1

Otra solución:

if val.to_i == 0
  # do stuff
end
Scott
fuente
1
Esto solo funciona si val es un número entero o nulo; 0.5.to_i == 0, any_string.to_i == 0, etc.
Zach Langley
"5".to_i == 5, pero tienes más o menos razón. Inteligente pero en realidad no funciona.
Andrew Grimm
-3
val ||= 0
if val == 0
# do something here
end
lmumar
fuente
3
-1: ¿Qué pasa si vales necesario mantener el valor en ? Estás superando los datos de entrada con este método, y hay formas mejores y menos destructivas de verificar cero o cero.
Platinum Azure
3
¿Qué tal si (val || 0) .zero?
Timkay