¿Hay una comparación Operador que devuelve verdad si ambos valores están fijados e iguales: falso si ambos son nil

Hay una manera ideomática o más corta de "compare dos valores pero no permita que sean nil".

Una manera sería:

(!left.nil? || !right.nil?) && (left == right)

Uno de los cheques de cero podría ser omitido, aquí, pero lo dejé en el ejemplo, para mostrar mejor la intención.

¿Hay un método en el núcleo de Ruby, en el núcleo oObjeto Básico, junto a equal?, eql?, == ¿Y eso coincide con esto?

Apunto a algo similar a lo que por ejemplo minitest assert_equal (editado para brevedad, nil-check en línea 5):

def assert_equal exp, act, msg = nil
  msg = message(msg, E) { diff exp, act }
  result = assert exp == act, msg

  if nil == exp then
    refute_nil exp, "Use assert_nil if expecting nil."
  end

  result
end

En mi caso esto es para un sistema de autorización donde repetidamente tengo que proteger contra ambos lados siendo nil. Por ejemplo.

class ContactPolicy < ApplicationPolicy
  def add?
    !actor.id.nil? && subject.id == actor.id
  end
end

Después de repetir ese patrón en varias formas, me preguntaba si no hay un mejor Ruby-ism para esto. Ahora me estoy inclinando hacia una refactorización usando null-objects que tienen un def ==(other) que siempre vuelve falso. La cuestión de si este "igual pero no nil" sigue siendo interesante.

Pregunta hecha hace 3 años, 5 meses, 0 días - Por codemaster89


3 Respuestas:

  • Posibles enfoques

    La respuesta óptima dependerá de ¿Por qué? usted piensa que el valor puede ser cero, y otros aspectos de su código que no se muestran en su pregunta original. Sin ese contexto, parece que se trata principalmente de un intento de proteger contra valores nulos que elevan una excepción NameError, al tiempo que evita que su expresión se resuelva a (nil == nil) == true. Si ese es el caso, puede tomar uno de los siguientes enfoques, entre otros:

    1. Rescate todas las excepciones posibles.
    2. Rescate NombreError explícitamente.
    3. Evite el manejo de la excepción y use una cadena de expresión condicional para probar si sus variables están definidas y no son peligrosas.

    Nombre del mango Excepciones de error de variables no definidas

    Usted podría rescatar todas las excepciones, pero esto generalmente se considera un enfoque pobre en el caso general.

    # Swallow anything descended from Exception. This is 
    # common in the wild, so it's idiomatic by definition,
    # but it can cast too wide of a net sometimes, and may
    # lead to unexpected problems.
    left == right rescue nil
    

    Sería mejor rescatar a NameError, pero depende de usted averiguar cómo el programa debe manejar la excepción. Tal vez decida establecer las variables, y luego retry el bloque rescatado, pero ciertamente hay otros enfoques.

    # Rescue just NameError; other exceptions would still
    # get raised when necessary.
    begin
      left == right
    rescue NameError => e
      # handle the exception
    end
    

    Evitar variables no definidas

    Hay varias situaciones en las que Ruby autovivificará las variables dentro de un alcance determinado, pero su ejemplo no es uno de ellos. En este caso, puede confiar en la precedencia del operador para asegurar que sus valores se definen utilizando el ¿Definido? palabra clave.

    Por ejemplo, asumiendo izquierda y derecho o undefined? o puede Responder_to? :nil?, entonces puede utilizar la siguiente expresión para:

    1. Asegurar ambos izquierda y derecho se definen. Devoluciones "expression" si ambos son variables definidas dentro de un array literal.
    2. Garantizar ninguno de los valores [left, right] son nil utilizando Enumerable #none? (que se mezcla en Array) y ¿Protesto? método que Array hereda.
    3. Compruebe la igualdad de sus dos variables definidas, no nil.
    defined? [left, right] and
      [left, right].none? &:nil? and
      left == right
    #=> nil
    

    Basado en su pregunta, la expectativa es que cuando sus variables resuelven nil == nil toda la expresión debe regresar false. La misma expresión condicional también protege contra ambas variables que se establecen nil en lugar de simplemente ser indefinido. Por ejemplo:

    # Test with nil values.
    left, right = nil, nil
    
    defined? [left, right] and
      [left, right].none? &:nil? and
      left == right
    #=> false
    

    Respondida el Dec 16, 2020 a las 13:28 - por gitguru

    Votos positivos: 0 | Votos negativos: 0

  • Sí, en Ruby hay un método que puedes utilizar para comparar dos valores y asegurarte de que ninguno de ellos sea nil: se trata del operador de comparación <=> (también conocido como spaceship operator).

    Este operador devuelve -1, 0 o 1 dependiendo de si el objeto en el lado izquierdo es menor que, igual a, o mayor que el objeto en el lado derecho. Además, este operador maneja automáticamente el caso en el que alguno de los valores comparados sea nil.

    Por lo tanto, podrías utilizar el operador <=> de la siguiente manera:

    ruby
    (left <=> right) == 0
    

    Esto te devolverá true si ambos valores son iguales y ninguno de ellos es nil, y false en caso contrario.

    Es importante tener en cuenta que el operador <=> debe estar definido para los objetos que estés comparando, ya que en algunos casos puede tener un comportamiento predeterminado que no sea el deseado.

    ¡Espero que esta información te sea útil para simplificar tu comparación y evitar chequeos innecesarios de nil en tus condicionales!

    Respondida el Dec 17, 2020 a las 13:30 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • En Ruby, no existe un operador de comparación que devuelva verdadero si ambos valores están configurados e iguales, y falso si ambos son nulos.

    Sin embargo, hay una forma más corta e idiomática de comparar dos valores pero no permitir que sean nulos:

    ruby
    left&.==(right)
    

    Este código primero verifica si left no es nulo y, de ser así, llama al método == en left pasándole right como argumento. Si left es nulo, la expresión devuelve nil.

    Por ejemplo:

    ruby
    left = nil
    right = 1
    
    left&.==(right) 

    nil

    En tu ejemplo de política de autorización, podrías usar este código para protegerte contra ambos lados siendo nulos:

    ruby
    class ContactPolicy < ApplicationPolicy
      def add?
        actor.id&.==(subject.id)
      end
    

    Este código primero verifica si actor.id no es nulo y, de ser así, llama al método == en actor.id pasándole subject.id como argumento. Si actor.id es nulo, la expresión devuelve nil y el método <h6>add? devolverá false. Espero que esto ayude!

    Respondida el Dec 17, 2020 a las 13:32 - por Gemini

    Votos positivos: 0 | Votos negativos: 0