在 Ruby 中,!! 的含义是什么?

140

想知道 Ruby 中的 !! 是什么。

8个回答

169

不是不是。

它用于将一个值转换为布尔值:

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

通常情况下不必使用 though,因为在 Ruby 中唯一的假值是 nilfalse,所以最好让这个约定保持不变。

可以把它看作是

!(!some_val)

它合法使用的一个方面是防止返回大量的数据。例如,您可能不想在has_image?方法中返回3MB的图像数据,或者您可能不想在logged_in?方法中返回整个用户对象。使用!!将这些对象转换为简单的true/false


8
使用这种方式并不是一种糟糕的做法。在谓词(以问号结尾的方法)中经常使用双感叹号来返回明确的布尔值。 - Swanand
6
Alex,你不需要担心返回大型对象的问题,因为Ruby会返回一个引用而不是对象的副本。 - DSimon
10
@DSimon,有时候你确实需要担心大对象,比如在打印或调试期间记录数值。 - Wayne Conrad
为什么不直接返回 .nil? 而不是使用 !!?这两者有区别吗? - ashes999
4
当你的值是布尔类型时,有一个区别。 !!true #=> truetrue.nil? #=> false - Alex Wayne
双重否定几乎从来不是必要的。Ruby解释器将始终以相同的方式计算表达式的真值,无论是否使用双重否定。因此,双重否定只会引入更多的计算,而没有技术上的收益。(收益在于取悦[某些]人类思维。)除了Wayne Conrad关于日志记录的观点之外:实际上有至少一个地方需要双重否定:当您转换为to_json时,nil将变为null,因此如果您希望该值在JSON中实际上为true或false,则需要双重否定。 - Pistos

31

如果右侧的对象不是nilfalse,则返回true;如果右侧的对象是nilfalse,则返回false

def logged_in?   
  !!@current_user
end

1
它是不是很明显来自Restful_Auth?!:D - Cameron

14

!代表取反布尔值状态,两个!没有什么特殊的意义,除了表示双重否定。

!表示对一个布尔值进行取反操作,即将true变为false,将false变为true。两个!并不会有其他特殊的含义,仅仅表示双重否定,即将一个布尔值取反两次,得到与原始值相同的结果。

!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,那么最好做最接近的事情...我想。 - Carson Myers

6
请注意,这个习惯用语在其他编程语言中也存在。C语言没有内置的bool类型,因此所有布尔值都被定义为int类型,并具有规范值01。以这个例子为例(为了清晰起见添加了括号):
!(1234) == 0
!(0) == 1
!(!(1234)) == 1

“非非”语法将任何非零整数转换为1,即规范的布尔值真。

总的来说,我发现使用合理的比较要比使用这种不常用的习惯语法更好:

int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious

或者只是 if (x)。!! 在需要获取 0/1 时非常有用。你可以考虑使用 (x) ? 1 : 0,也可以不考虑。 - derobert

3

这是一个"双重否定",但实践中被不鼓励使用。如果您正在使用rubocop,您会看到它抱怨这样的代码违反了Style/DoubleNegation规定。

该原则指出:

由于这既晦涩难懂又通常是多余的,因此应避免使用 [然后转述:]将!!something更改为!something.nil?


1
但是... !!false # => false!false.nil? # => true - Doug
@Doug 如果你知道你有一个布尔值,那么它是多余的(只需使用该值)。如果你不知道,你可以使用!(foo.nil? || foo == false)——更冗长,是的,但更少含糊。 - David Moles

3

如果你需要进行异或操作,这将非常有用。以下是稍作修改的Matt Van Horn的答案:

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

!!1 ^ !!true
=> false

我用它来确保两个变量要么都是nil,要么都不是nil。

raise "Inconsistency" if !!a ^ !!b

0

如果你需要将枚举转换为布尔值,了解它的工作原理可能会很有用。我有一段代码可以使用 classy_enum gem 来实现这一点:

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 的方法。


-1
其他答案已经讨论了!!的作用以及它是否是一种好的编程实践。
然而,这些答案都没有给出将值转换为布尔类型的“标准Ruby方式”。
true & variable

TrueClass,Ruby值true的类,实现了一个方法&文档如下:

如果objnilfalse,则返回false,否则返回true

当标准库已经为你提供了支持时,为什么要使用不洁的双重否定呢?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接