Что значит !! значит в рубине?

Ответы:

161

Нет нет. Он используется для преобразования значения в логическое значение:

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

Обычно это не нужно использовать, поскольку единственными ложными значениями для Ruby являются nilи false, поэтому обычно лучше оставить это соглашение в силе.

Думайте об этом как

!(!some_val)

Одна вещь, для которой он используется на законных основаниях - это предотвращение возврата огромного куска данных. Например, вы, вероятно, не хотите возвращать 3 МБ данных изображения в вашем has_image?методе, или вы не хотите возвращать весь пользовательский объект в logged_in?методе. Использование !!преобразует эти объекты в простой true/ false.

Алекс Уэйн
источник
8
Это не плохая практика, чтобы использовать это. Двойной взрыв часто используется в предикатах (методы, заканчивающиеся на?) Для возврата явно логического значения.
Суонанд
6
Алекс, тебе не нужно беспокоиться о возврате больших объектов, так как Ruby вернет ссылку, а не копию объекта.
DSimon
10
@DSimon, иногда вам нужно беспокоиться о больших объектах, например, при печати или регистрации значения во время отладки.
Уэйн Конрад
Почему бы просто не вернуться .nil?вместо использования !!? Есть ли разница?
ashes999
3
Есть разница, когда ваша ценность - булевская. !!true #=> trueиtrue.nil? #=> false
Алекс Уэйн
31

Возвращается, trueесли объекта справа нет nilи нет false, falseесли он есть nilилиfalse

def logged_in?   
  !!@current_user
end
RichH
источник
1
Было ли это очевидно, что это произошло от Restful_Auth ?! : D
Кэмерон
14

!означает отрицание логического состояния, два !s ничего особенного, кроме двойного отрицания.

!true == false
# => true

Обычно используется, чтобы заставить метод возвращать логическое значение. Он обнаружит любую правдивость, такую ​​как строка, целые числа, а что нет, и превратит ее в логическое значение.

!"wtf"
# => false

!!"wtf"
# => true

Более реальный вариант использования:

def title
  "I return a string."
end

def title_exists?
  !!title
end

Это полезно, когда вы хотите убедиться, что логическое значение возвращается. ИМХО это вроде бессмысленно, хотя, видя , что оба , if 'some string'и if trueточно такой же поток, но некоторые люди считают полезным явно возвращать логическое значение.

Август Лиллеас
источник
мне кажется, это просто более быстрый способ, чем использование оператора if или троичного оператора. Поскольку вы не можете просто вернуться title, можете также сделать самое близкое к этому ... Я полагаю
Карсон Майерс
6

Обратите внимание, что эта идиома существует и в других языках программирования. C не имеет встроенного boolтипа, поэтому все логические значения были напечатаны как intвместо, с каноническими значениями 0или 1. Возьмем этот пример (скобки добавлены для ясности):

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

Синтаксис not-not преобразует любое ненулевое целое число 1в каноническое логическое истинное значение.

В целом, однако, я считаю, что гораздо лучше провести разумное сравнение, чем использовать эту необычную идиому:

int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
Том
источник
Или просто если (х). !! полезно, когда вам нужно получить 0/1. Вы можете или не можете рассмотреть (х)? 1: 0 яснее.
Дероберт
3

Это полезно, если вам нужно сделать эксклюзив или . Копирование ответа Мэтта Ван Хорна с небольшими изменениями:

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

!!1 ^ !!true
=> false

Я использовал его, чтобы убедиться, что две переменные либо равны нулю, либо обе не равны нулю.

raise "Inconsistency" if !!a ^ !!b
Эндрю Гримм
источник
3

Это «двойной негатив», но практика не приветствуется. Если вы используете rubocop , вы увидите, что он жалуется на такой код с Style/DoubleNegationнарушением.

В обоснование состояния:

Поскольку это так загадочно и обычно излишним, его следует избегать [тогда перефразирую:] Изменение !!somethingв!something.nil?

Мика Эллиотт
источник
1
Но ... !!false # => falseпока!false.nil? # => true
Дуг
@Doug Если вы знаете, что у вас есть логическое значение, оно избыточно (просто используйте значение). Если вы этого не сделаете, вы можете использовать !(foo.nil? || foo == false)- более многословно, да, но менее загадочно.
Дэвид Моулз
0

Понимание того, как это работает, может быть полезно, если вам нужно преобразовать, скажем, перечисление в логическое значение. У меня есть код, который делает именно это, используя classy_enumгем:

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

Тогда в сервисном коде у меня есть, например:

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

Фактически оператор bangbang стал тем, что я мог бы написать как метод to_bool.

inopinatus
источник