Que hace !! significa en rubí?

134

Me pregunto qué !!hay en Ruby.

Cameron
fuente

Respuestas:

161

No no Se utiliza para convertir un valor a un valor booleano:

!!nil   #=> false
!!"abc" #=> true
!!false #=> false

Sin embargo, generalmente no es necesario usarlo, ya que los únicos valores falsos para Ruby son nily false, por lo que generalmente es mejor dejar que esa convención permanezca.

Piensa en ello como

!(!some_val)

Una cosa para la que se usa legítimamente es evitar que se devuelva una gran cantidad de datos. Por ejemplo, probablemente no desee devolver 3 MB de datos de imagen en su has_image?método, o tal vez no desee devolver todo su objeto de usuario en el logged_in?método. El uso !!convierte estos objetos a un simple true/ false.

Alex Wayne
fuente
8
No es una mala práctica usar esto. Double bang se usa a menudo en predicados (métodos que terminan con?) Para devolver un valor booleano explícito.
Swanand
66
Alex, realmente no debes preocuparte por devolver objetos grandes, ya que Ruby devolverá una referencia, no una copia del objeto.
DSimon
10
@DSimon, a veces debe preocuparse por los objetos grandes, como al imprimir o registrar un valor durante la depuración.
Wayne Conrad
¿Por qué no simplemente regresar en .nil?lugar de usar !!? ¿Hay una diferencia?
cenizas999
3
Hay una diferencia cuando su valor es un booelan. !!true #=> trueytrue.nil? #=> false
Alex Wayne
31

Regresa truesi el objeto de la derecha no es nily no false, falsesi es nilofalse

def logged_in?   
  !!@current_user
end
RichH
fuente
1
¿Era obvio que provenía de Restful_Auth? : D
Cameron
14

!significa negar el estado booleano, dos !s no es nada especial, aparte de una doble negación.

!true == false
# => true

Se usa comúnmente para forzar a un método a devolver un valor booleano. Detectará cualquier tipo de veracidad, como cadenas, enteros y demás, y lo convertirá en un booleano.

!"wtf"
# => false

!!"wtf"
# => true

Un caso de uso más real:

def title
  "I return a string."
end

def title_exists?
  !!title
end

Esto es útil cuando desea asegurarse de que se devuelve un valor booleano. En mi humilde opinión es una especie de sentido, sin embargo, al ver que tanto if 'some string'y if truees el mismo caudal exacto, pero algunas personas les resulta útil para volver explícitamente un valor lógico.

August Lilleaas
fuente
me parece una forma más rápida que usar una declaración if o un operador ternario. Ya que no puedes regresar title, también puedes hacer lo más parecido a eso ... supongo
Carson Myers
6

Tenga en cuenta que este idioma también existe en otros lenguajes de programación. C no tenía un booltipo intrínseco , por lo que todos los booleanos se escribieron como en su intlugar, con valores canónicos de 0o 1. Toma este ejemplo (paréntesis agregados para mayor claridad):

!(1234) == 0
!(0) == 1
!(!(1234)) == 1

La sintaxis "no-no" convierte cualquier número entero distinto de cero en 1el valor verdadero booleano canónico.

En general, sin embargo, me parece mucho mejor hacer una comparación razonable que usar este idioma poco común:

int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
Tom
fuente
O solo si (x). !! es útil cuando necesita obtener 0/1. ¿Puede o no considerar (x)? 1: 0 más claro.
derobert
3

Es útil si necesita hacer una exclusiva o . Copiando la respuesta de Matt Van Horn con ligeras modificaciones:

1 ^ true
TypeError: can't convert true into Integer

!!1 ^ !!true
=> false

Lo usé para asegurarme de que dos variables fueran nulas o ambas no nulas.

raise "Inconsistency" if !!a ^ !!b
Andrew Grimm
fuente
3

Es "doble negativo", pero se desalienta la práctica. Si está usando rubocop , verá que se queja en dicho código con unStyle/DoubleNegation violación.

La justificación dice:

Como esto es críptico y generalmente redundante, se debe evitar [parafraseando:] Cambiar !!somethinga!something.nil?

Micah Elliott
fuente
1
Pero ... !!false # => falsemientras!false.nil? # => true
Doug
@Doug Si sabe que tiene un valor booleano, es redundante (solo use el valor). Si no lo hace, puede usar !(foo.nil? || foo == false): más detallado, sí, pero menos críptico.
David Moles
0

Comprender cómo funciona puede ser útil si necesita convertir, por ejemplo, una enumeración en un booleano. Tengo un código que hace exactamente eso, usando la classy_enumgema:

class LinkStatus < ClassyEnum::Base
  def !
    return true
  end
end

class LinkStatus::No < LinkStatus
end

class LinkStatus::Claimed < LinkStatus
  def !
    return false
  end
end

class LinkStatus::Confirmed < LinkStatus
  def !
    return false
  end
end

class LinkStatus::Denied < LinkStatus
end

Luego, en el código de servicio que tengo, por ejemplo:

raise Application::Error unless !!object.link_status   # => raises exception for "No" and "Denied" states.

Efectivamente, el operador bangbang se ha convertido en lo que de otro modo podría haber escrito como un método llamado to_bool.

inopinatus
fuente