Que fait !! signifie en rubis?

134

Je me demande simplement ce qu'il !!y a dans Ruby.

Cameron
la source

Réponses:

161

Pas pas. Il est utilisé pour convertir une valeur en booléen:

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

Il n'est généralement pas nécessaire de l'utiliser car les seules fausses valeurs de Ruby sont nilet false, il est donc généralement préférable de laisser cette convention en vigueur.

Pensez-y comme

!(!some_val)

Une chose à laquelle il est légitimement utilisé est d'empêcher le retour d'une énorme quantité de données. Par exemple, vous ne souhaitez probablement pas renvoyer 3 Mo de données d'image dans votre has_image?méthode, ou vous ne souhaitez peut-être pas renvoyer l'intégralité de votre objet utilisateur dans la logged_in?méthode. Utiliser !!convertit ces objets en un simple true/ false.

Alex Wayne
la source
8
Ce n'est pas une mauvaise pratique de l'utiliser. Le double bang est souvent utilisé dans les prédicats (méthodes se terminant par?) Pour renvoyer une valeur explicitement booléenne.
Swanand
6
Alex, vous n'avez pas vraiment à vous soucier de renvoyer des objets volumineux, car Ruby renverra une référence, pas une copie de l'objet.
DSimon
10
@DSimon, vous devez parfois vous soucier des objets volumineux, comme lors de l'impression ou de la journalisation d'une valeur lors du débogage.
Wayne Conrad
Pourquoi ne pas simplement revenir .nil?au lieu d'utiliser !!? Y a-t-il une différence?
ashes999
3
Il y a une différence lorsque votre valeur est un booelan. !!true #=> trueettrue.nil? #=> false
Alex Wayne
31

Il renvoie truesi l'objet de droite n'est pas nilet non false, falses'il est niloufalse

def logged_in?   
  !!@current_user
end
RichH
la source
1
Était-ce évident que cela venait de Restful_Auth?! : D
Cameron
14

!signifie nier l'état booléen, deux !s n'ont rien de spécial, à part une double négation.

!true == false
# => true

Il est couramment utilisé pour forcer une méthode à renvoyer un booléen. Il détectera tout type de véracité, comme une chaîne, des entiers et autres, et le transformera en booléen.

!"wtf"
# => false

!!"wtf"
# => true

Un cas d'utilisation plus réel:

def title
  "I return a string."
end

def title_exists?
  !!title
end

Ceci est utile lorsque vous voulez vous assurer qu'un booléen est renvoyé. IMHO, c'est un peu inutile, cependant, vu que les deux if 'some string'et que if truec'est exactement le même flux, mais certaines personnes trouvent utile de renvoyer explicitement un booléen.

August Lilleaas
la source
cela me semble être un moyen plus rapide que d'utiliser une instruction if ou un opérateur ternaire. Puisque vous ne pouvez pas simplement revenir title, autant faire la chose la plus proche ... Je suppose
Carson Myers
6

Notez que cet idiome existe également dans d'autres langages de programmation. C n'avait pas de booltype intrinsèque , donc tous les booléens ont été typés comme à la intplace, avec des valeurs canoniques de 0ou 1. Prend cet exemple (parenthèses ajoutées pour plus de clarté):

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

La syntaxe "not-not" convertit tout entier différent de zéro en 1la valeur vraie booléenne canonique.

En général, cependant, je trouve beaucoup mieux de faire une comparaison raisonnable que d'utiliser cet idiome inhabituel:

int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
À M
la source
Ou juste si (x). !! est utile lorsque vous devez obtenir soit 0/1. Vous pouvez ou non envisager (x)? 1: 0 plus clair.
derobert
3

C'est utile si vous avez besoin de faire un ou exclusif . Copie de la réponse de Matt Van Horn avec de légères modifications:

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

!!1 ^ !!true
=> false

Je l'ai utilisé pour m'assurer que deux variables étaient soit nulles, soit toutes deux non nulles.

raise "Inconsistency" if !!a ^ !!b
Andrew Grimm
la source
3

C'est "double négatif", mais la pratique est découragée. Si vous utilisez rubocop , vous le verrez se plaindre d'un tel code avec unStyle/DoubleNegation violation.

La justification stipule:

Comme c'est à la fois cryptique et généralement redondant, il faut éviter [puis paraphraser:] Remplacer !!somethingpar!something.nil?

Micah Elliott
la source
1
Mais ... !!false # => falsependant que!false.nil? # => true
Doug
@Doug Si vous savez que vous avez une valeur booléenne, c'est redondant (utilisez simplement la valeur). Sinon, vous pouvez utiliser !(foo.nil? || foo == false)- plus verbeux, oui, mais moins cryptique.
David Moles
0

Comprendre comment cela fonctionne peut être utile si vous devez convertir, par exemple, une énumération en booléen. J'ai du code qui fait exactement cela, en utilisant le classy_enumgem:

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

Ensuite, dans le code de service, j'ai, par exemple:

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

En fait, l'opérateur bangbang est devenu ce que j'aurais pu autrement écrire comme une méthode appelée to_bool.

inopinatus
la source