我看了一下Ruby on Rails的源代码,发现了这样的代码:
case options
when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
...
when String
...
when :back
...
when Proc
...
end
其中options
可以是String
,Symbol
,Proc
或Hash
对象。 ===
比较仅在一种情况下返回true
:
'string' === /string/ # => false
:back === :back # => true
(Proc.new {}) === Proc # => nil
Hash.new === Hash # => false
Ruby case
如何工作,允许匹配这些不同的情况?
答案 0 :(得分:4)
您(以及许多初学者)似乎做出的错误假设是===
是对称的。实际上并非如此。 x === y
的工作方式不同,取决于y
,但取决于x
。
不同的类对===
有不同的定义。表达式x === y
与:
y == x
(适用于x
:String
,Symbol
,Hash
等的实例。)y =~ x
(适用于x
:Regexp
的实例)y.kind_of?(x)
(适用于x
:Class
的实例)此外,您可能会混淆一个类及其实例。 /regexp/ ===
与Regexp ===
不同。 "string" ===
与String ===
等不同。
答案 1 :(得分:3)
可能有助于理解:
"foo" === /foo/
实际上是另一种写作方式:
"foo".===(/foo/)
所以它实际上是对"foo"
上的实例方法String#===
的方法调用(它是类String
的一个实例),并将其作为参数传递给它/foo/
。因此,将发生的事情完全由String#===
定义。
在您的代码中,实际发生的是:
if /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i === options
# ...
elsif String === options
# ...
elsif :back === options
# ...
elsif Proc === options
# ...
else
# ...
end.delete("\0\r\n")
因此,您的case
语句实际上是对(按出现顺序)的方法调用:
Regex#===
Module#===
Symbol#===
Module#===
关于你问题中的第二个例子:
'string' === /string/ # => false
以上是调用String#===
,根据文档:
返回str == obj,类似于Object#==。
如果obj不是String的实例但是响应to_str,那么 使用大小写等式Object#===。
比较两个字符串否则,返回类似于#eql ?,比较长度和内容。
这就是为什么它不匹配,因为它实际上调用了Object#==
。
类似地:
# this calls `Proc#===`, not `Module#===`
(Proc.new {}) === Proc # => false
# however, if you want to test for class equality you should do:
Proc === (Proc.new {}) # => true, calls `Module#===`
同样适用于Hash.new === Hash
。