Co robi !! znaczy w rubinie?

134

Zastanawiam się tylko, co !!jest w Rubim.

Cameron
źródło

Odpowiedzi:

161

Nie nie. Służy do konwersji wartości na boolean:

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

Zwykle nie jest to konieczne, ponieważ jedynymi fałszywymi wartościami dla Rubiego są nili false, więc zazwyczaj najlepiej jest pozostawić tę konwencję.

Pomyśl o tym jako

!(!some_val)

Jedną z rzeczy, do których jest legalnie używany, jest zapobieganie zwracaniu ogromnej ilości danych. Na przykład prawdopodobnie nie chcesz zwracać 3 MB danych obrazu w swojej has_image?metodzie lub możesz nie chcieć zwracać całego obiektu użytkownika w logged_in?metodzie. Użycie !!konwertuje te obiekty na proste true/ false.

Alex Wayne
źródło
8
Używanie tego nie jest złą praktyką. Podwójny huk jest często używany w predykatach (metodach kończących się na?), Aby zwrócić jawnie wartość logiczną.
Swanand
6
Alex, naprawdę nie musisz się martwić o zwrócenie dużych obiektów, ponieważ Ruby zwróci odniesienie, a nie kopię obiektu.
DSimon
10
@DSimon, czasami musisz się martwić dużymi obiektami, na przykład podczas drukowania lub rejestrowania wartości podczas debugowania.
Wayne Conrad
Dlaczego nie po prostu wrócić .nil?zamiast używać !!? Czy jest jakaś różnica?
popiół 9
3
Istnieje różnica, gdy twoja wartość to booelan. !!true #=> trueoraztrue.nil? #=> false
Alex Wayne,
31

Zwraca, truejeśli obiekt po prawej stronie nie jest nili nie jest false, falsejeśli jest nillubfalse

def logged_in?   
  !!@current_user
end
RichH
źródło
1
Czy to oczywiste, że pochodziło z Restful_Auth ?! : D
Cameron,
14

!oznacza zanegowanie stanu logicznego, dwa !s to nic specjalnego, poza podwójną negacją.

!true == false
# => true

Jest powszechnie używany, aby wymusić na metodzie zwrócenie wartości logicznej. Wykryje każdy rodzaj prawdziwości, taki jak ciąg, liczby całkowite i inne, i zamieni go na wartość logiczną.

!"wtf"
# => false

!!"wtf"
# => true

Bardziej realny przypadek użycia:

def title
  "I return a string."
end

def title_exists?
  !!title
end

Jest to przydatne, gdy chcesz się upewnić, że zwracana jest wartość logiczna. IMHO jest to trochę bezcelowe, biorąc pod uwagę, że oba if 'some string'i if truesą dokładnie tym samym przepływem, ale niektórzy ludzie uważają, że przydatne jest jawne zwrócenie wartości logicznej.

August Lilleaas
źródło
wydaje mi się, że jest to po prostu szybszy sposób niż użycie instrukcji if lub operatora trójskładnikowego. Ponieważ nie możesz po prostu wrócić title, równie dobrze możesz zrobić najbliższą rzecz ... przypuszczam
Carson Myers
6

Zauważ, że ten idiom istnieje również w innych językach programowania. C nie ma typu wewnętrznego bool, więc wszystkie wartości logiczne zostały wpisane jako intzamiast tego, z wartościami kanonicznymi 0lub 1. Przyjmuje ten przykład (dla przejrzystości dodano nawiasy):

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

Składnia „not-not” konwertuje dowolną niezerową liczbę całkowitą 1na kanoniczną logiczną wartość true.

Ogólnie jednak uważam, że znacznie lepiej jest dokonać rozsądnego porównania niż użyć tego niecodziennego idiomu:

int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
Tomek
źródło
Lub po prostu jeśli (x). !! przydaje się, gdy musisz uzyskać 0/1. Możesz lub nie rozważyć (x)? 1: 0 wyraźniej.
derobert
3

Jest to przydatne, jeśli chcesz zrobić ekskluzywną lub . Na podstawie odpowiedzi Matta Van Horna z niewielkimi modyfikacjami:

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

!!1 ^ !!true
=> false

Użyłem go, aby upewnić się, że dwie zmienne są albo zerowe, albo obie nie są zerowe.

raise "Inconsistency" if !!a ^ !!b
Andrew Grimm
źródło
3

Jest to „podwójnie negatywne”, ale praktyka jest odradzana. Jeśli używasz rubocop , zobaczysz, że narzeka na taki kod z Style/DoubleNegationnaruszeniem.

W Uzasadnienie stanowi:

Ponieważ jest to zarówno tajemnicze, jak i zwykle zbędne, należy tego unikać [następnie parafrazując:] Zmień !!somethingna!something.nil?

Micah Elliott
źródło
1
Ale ... !!false # => falsepodczas gdy!false.nil? # => true
Doug,
@Doug Jeśli wiesz, że masz wartość logiczną, jest ona zbędna (po prostu użyj wartości). Jeśli tego nie zrobisz, możesz użyć !(foo.nil? || foo == false)- bardziej rozwlekły, tak, ale mniej tajemniczy.
David Moles
0

Zrozumienie, jak to działa, może być przydatne, jeśli chcesz przekonwertować, powiedzmy, wyliczenie na wartość logiczną. Mam kod, który robi dokładnie to, używając classy_enumklejnotu:

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

Wówczas w kodzie serwisowym mam np:

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

W rzeczywistości operator bangbang stał się tym, co inaczej mógłbym napisać jako metodę o nazwie to_bool.

inopinatus
źródło