提问人:denniss 提问时间:8/23/2011 最后编辑:Freedom_Bendenniss 更新时间:6/2/2021 访问量:227527
equal?、eql?、=== 和 == 有什么区别?
What's the difference between equal?, eql?, ===, and ==?
问:
我试图理解这四种方法之间的区别。我知道默认情况下,当两个操作数引用完全相同的对象时,它会调用返回 true 的方法。==
equal?
===
默认情况下还调用哪些调用...好的,所以如果这三种方法都没有被覆盖,那么我猜,并做完全相同的事情?==
equal?
===
==
equal?
现在来了.这(默认情况下)有什么作用?它是否调用操作数的哈希/ID?eql?
为什么 Ruby 有这么多等号?它们应该在语义上有所不同吗?
答:
我将在这里大量引用 Object 文档,因为我认为它有一些很好的解释。我鼓励您阅读它,以及这些方法的文档,因为它们在其他类(如 String)中被覆盖。
旁注:如果您想在不同的对象上亲自尝试这些,请使用如下方法:
class Object
def all_equals(o)
ops = [:==, :===, :eql?, :equal?]
Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })]
end
end
"a".all_equals "a" # => {"=="=>true, "==="=>true, "eql?"=>true, "equal?"=>false}
==
— 通用的“平等”
在对象级别,仅当 和 是同一对象时才返回 true。通常,此方法在后代类中重写,以提供特定于类的含义。
==
obj
other
这是最常见的比较,因此也是您(作为类的作者)决定两个对象是否“相等”的最基本的地方。
===
— 大小写相等
对于类 Object,实际上与调用相同,但通常由后代覆盖,以在 case 语句中提供有意义的语义。
#==
这非常有用。具有有趣实现的事物示例:===
- 范围
- 正则表达式
- Proc(在 Ruby 1.9 中)
因此,您可以执行以下操作:
case some_object
when /a regex/
# The regex matches
when 2..4
# some_object is in the range 2..4
when lambda {|x| some_crazy_custom_predicate }
# the lambda returned true
end
请在此处查看我的答案,以获取 + 如何使代码更简洁的简洁示例。当然,通过提供自己的实现,您可以获得自定义语义。case
Regex
===
case
eql?
— 平等Hash
如果 和 引用相同的哈希键,则该方法返回 true。这用于测试成员是否相等。对于
类 Object
的对象,eql?
与==
同义。子类通常通过为其重写的方法添加别名来延续这一传统,但也有例外。 例如,类型在 之间执行类型转换,但不跨 执行类型转换,因此:eql?
obj
other
Hash
eql?
==
Numeric
==
eql?
1 == 1.0 #=> true 1.eql? 1.0 #=> false
因此,您可以自由地覆盖它以供自己使用,也可以覆盖和使用,以便这两种方法的行为方式相同。==
alias :eql? :==
equal?
— 身份比较
与 不同的是,该方法永远不应被子类覆盖:它用于确定对象标识(即,iff 与 是相同的对象)。
==
equal?
a.equal?(b)
a
b
这实际上是指针比较。
评论
Numeric
==
===
case
===
=== #---大小写相等
== #--- 通用平等
两者的工作方式相似,但“===”甚至做大小写语句
"test" == "test" #=> true
"test" === "test" #=> true
这里的区别
String === "test" #=> true
String == "test" #=> false
评论
a==b
a===b
a===b
===
a===b
b===a
a==b
我为上述所有内容写了一个简单的测试。
def eq(a, b)
puts "#{[a, '==', b]} : #{a == b}"
puts "#{[a, '===', b]} : #{a === b}"
puts "#{[a, '.eql?', b]} : #{a.eql?(b)}"
puts "#{[a, '.equal?', b]} : #{a.equal?(b)}"
end
eq("all", "all")
eq(:all, :all)
eq(Object.new, Object.new)
eq(3, 3)
eq(1, 1.0)
我喜欢 jtbandes 答案,但由于它很长,我将添加我自己的紧凑答案:
==
, ===
, eql
?, equal?
是 4 个比较器,即。在 Ruby 中比较 2 个对象的 4 种方法。
因为在 Ruby 中,所有的比较器(以及大多数运算符)实际上都是方法调用,你可以自己更改、覆盖和定义这些比较方法的语义。然而,重要的是要理解,当 Ruby 的内部语言结构使用哪个比较器时:
==
(值比较)
Ruby 在任何地方都使用 :== 来比较 2 个对象的值,例如。哈希值:
{a: 'z'} == {a: 'Z'} # => false
{a: 1} == {a: 1.0} # => true
===
(大小写比较)
Ruby 在 case/when 构造中使用 :===。以下代码片段在逻辑上是相同的:
case foo
when bar; p 'do something'
end
if bar === foo
p 'do something'
end
eql
? (哈希键比较)
Ruby 使用 :eql?(结合方法哈希)来比较哈希键。在大多数类中:eql?与 :== 相同。
了解 :eql?仅当您想要创建自己的特殊类时才重要:
class Equ
attr_accessor :val
alias_method :initialize, :val=
def hash() self.val % 2 end
def eql?(other) self.hash == other.hash end
end
h = {Equ.new(3) => 3, Equ.new(8) => 8, Equ.new(15) => 15} #3 entries, but 2 are :eql?
h.size # => 2
h[Equ.new(27)] # => 15
注意:常用的 Ruby 类集也依赖于 Hash-key-comparison。
equal?
(对象标识比较)
Ruby 使用 :equal?检查两个对象是否相同。此方法(类 BasicObject)不应被覆盖。
obj = obj2 = 'a'
obj.equal? obj2 # => true
obj.equal? obj.dup # => false
评论
eql?
eql?
a.eql?(b)
a.hash == b.hash
bar === foo
foo === bar
bar === foo
Ruby 公开了几种不同的处理相等的方法:
a.equal?(b) # object identity - a and b refer to the same object
a.eql?(b) # object equivalence - a and b have the same value
a == b # object equivalence - a and b have the same value with type conversion.
点击下面的链接继续阅读,它给了我一个清晰的总结。
https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/equality-matchers
希望它能帮助到其他人。
相等运算符:== 和 !=
== 运算符,也称为相等或双相等,如果两个对象相等,则返回 true,如果两个对象不相等,则返回 false。
"koan" == "koan" # Output: => true
!= 运算符,也称为不等式,与 == 相反。如果两个对象不相等,则返回 true,如果它们相等,则返回 false。
"koan" != "discursive thought" # Output: => true
请注意,具有相同元素且顺序不同的两个数组不相等,同一字母的大写和小写版本不相等,依此类推。
在比较不同类型的数字(例如整数和浮点数)时,如果它们的数值相同,则 == 将返回 true。
2 == 2.0 # Output: => true
平等?
与测试两个操作数是否相等的 == 运算符不同,equal 方法检查两个操作数是否引用同一对象。这是 Ruby 中最严格的平等形式。
例: a = “禅” b = “禅”
a.object_id # Output: => 20139460
b.object_id # Output :=> 19972120
a.equal? b # Output: => false
在上面的示例中,我们有两个具有相同值的字符串。但是,它们是两个不同的对象,具有不同的对象 ID。因此,平等?方法将返回 false。
让我们再试一次,只是这次 b 将引用 a。请注意,两个变量的对象 ID 相同,因为它们指向同一个对象。
a = "zen"
b = a
a.object_id # Output: => 18637360
b.object_id # Output: => 18637360
a.equal? b # Output: => true
EQL?
在 Hash 类中,eql?方法 它用于测试密钥是否相等。需要一些背景来解释这一点。在一般的计算上下文中,哈希函数采用任何大小的字符串(或文件)并生成固定大小的字符串或整数,称为哈希码,通常称为哈希。一些常用的哈希码类型是 MD5、SHA-1 和 CRC。它们用于加密算法、数据库索引、文件完整性检查等。某些编程语言(如 Ruby)提供称为哈希表的集合类型。哈希表是类似字典的集合,它成对存储数据,由唯一键及其相应的值组成。在后台,这些密钥存储为哈希码。哈希表通常简称为哈希。请注意单词 hash 如何指代哈希码或哈希表。在 Ruby 编程的上下文中,hash 这个词几乎总是指类似字典的集合。
Ruby 提供了一个称为 hash 的内置方法,用于生成哈希码。在下面的示例中,它接受一个字符串并返回一个哈希码。请注意,具有相同值的字符串始终具有相同的哈希码,即使它们是不同的对象(具有不同的对象 ID)。
"meditation".hash # Output: => 1396080688894079547
"meditation".hash # Output: => 1396080688894079547
"meditation".hash # Output: => 1396080688894079547
哈希方法在 Kernel 模块中实现,该模块包含在 Object 类中,该类是所有 Ruby 对象的默认根。某些类(如 Symbol 和 Integer)使用默认实现,而其他类(如 String 和 Hash)则提供自己的实现。
Symbol.instance_method(:hash).owner # Output: => Kernel
Integer.instance_method(:hash).owner # Output: => Kernel
String.instance_method(:hash).owner # Output: => String
Hash.instance_method(:hash).owner # Output: => Hash
在 Ruby 中,当我们将某些东西存储在哈希(集合)中时,作为键(例如字符串或符号)提供的对象被转换并存储为哈希码。稍后,当从哈希(集合)中检索元素时,我们提供了一个对象作为键,该对象被转换为哈希码并与现有键进行比较。如果存在匹配项,则返回相应项的值。比较是使用 eql?引擎盖下的方法。
"zen".eql? "zen" # Output: => true
# is the same as
"zen".hash == "zen".hash # Output: => true
在大多数情况下,eql?方法的行为类似于 == 方法。但是,也有一些例外。例如,eql?将整数与浮点数进行比较时不执行隐式类型转换。
2 == 2.0 # Output: => true
2.eql? 2.0 # Output: => false
2.hash == 2.0.hash # Output: => false
大小写相等运算符:===
Ruby 的许多内置类,如 String、Range 和 Regexp,都提供了自己的 === 运算符实现,也称为大小写相等、三等或三等。由于它在每个类中的实现方式不同,因此它的行为会根据调用它的对象类型而有所不同。通常,如果右侧的对象“属于”或“是”左侧对象的成员,则返回 true。例如,它可用于测试对象是否是类(或其子类之一)的实例。
String === "zen" # Output: => true
Range === (1..2) # Output: => true
Array === [1,2,3] # Output: => true
Integer === 2 # Output: => true
使用其他可能最适合该工作的方法也可以获得相同的结果。通常最好在不牺牲效率和简洁性的情况下,通过尽可能明确地编写易于阅读的代码。
2.is_a? Integer # Output: => true
2.kind_of? Integer # Output: => true
2.instance_of? Integer # Output: => false
请注意,最后一个示例返回 false,因为像 2 这样的整数是 Fixnum 类的实例,该类是 Integer 类的子类。 ===、is_a?instance_of?如果对象是给定类或任何子类的实例,则方法返回 true。instance_of 方法更严格,仅当对象是该确切类的实例而不是子类时才返回 true。
is_a?kind_of?方法在 Kernel 模块中实现,该模块由 Object 类混合。两者都是同一方法的别名。让我们验证一下:
Kernel.instance_method(:kind_of?) == Kernel.instance_method(:is_a?) # 输出: => true
=== 的范围实现
在范围对象上调用 === 运算符时,如果右侧的值落在左侧的范围内,则返回 true。
(1..4) === 3 # Output: => true
(1..4) === 2.345 # Output: => true
(1..4) === 6 # Output: => false
("a".."d") === "c" # Output: => true
("a".."d") === "e" # Output: => false
请记住,=== 运算符调用左侧对象的 === 方法。所以 (1..4) === 3 等价于 (1..4).=== 3。换句话说,左侧操作数的类将定义将调用 === 方法的哪个实现,因此操作数位置不可互换。
=== 的正则表达式实现
如果右侧的字符串与左侧的正则表达式匹配,则返回 true。 /zen/ === “今天练习坐禅” # 输出: => true # 与 “今天练习坐禅”=~ /zen/
在 case/when 语句中隐式使用 === 运算符
此运算符也用于 case/when 语句的后台。这是它最常见的用途。
minutes = 15
case minutes
when 10..20
puts "match"
else
puts "no match"
end
# Output: match
在上面的示例中,如果 Ruby 隐式使用了双等运算符 (==),则范围 10..20 不会被视为等于整数,例如 15。它们之所以匹配,是因为在所有 case/when 语句中都隐式使用了三等运算符 (===)。上面示例中的代码等效于:
if (10..20) === minutes
puts "match"
else
puts "no match"
end
模式匹配运算符:=~ 和 !~
=~(等波浪号)和 !~(bang-tilde)运算符用于将字符串和符号与正则表达式模式进行匹配。
String 和 Symbol 类中 =~ 方法的实现需要正则表达式(Regexp 类的实例)作为参数。
"practice zazen" =~ /zen/ # Output: => 11
"practice zazen" =~ /discursive thought/ # Output: => nil
:zazen =~ /zen/ # Output: => 2
:zazen =~ /discursive thought/ # Output: => nil
Regexp 类中的实现需要字符串或符号作为参数。
/zen/ =~ "practice zazen" # Output: => 11
/zen/ =~ "discursive thought" # Output: => nil
在所有实现中,当字符串或符号与正则表达式模式匹配时,它会返回一个整数,该整数是匹配项的位置(索引)。如果没有匹配项,则返回 nil。请记住,在 Ruby 中,任何整数值都是“truthy”,nil 是“falsy”,因此 =~ 运算符可以在 if 语句和三元运算符中使用。
puts "yes" if "zazen" =~ /zen/ # Output: => yes
"zazen" =~ /zen/?"yes":"no" # Output: => yes
模式匹配运算符对于编写较短的 if 语句也很有用。例:
if meditation_type == "zazen" || meditation_type == "shikantaza" || meditation_type == "kinhin"
true
end
Can be rewritten as:
if meditation_type =~ /^(zazen|shikantaza|kinhin)$/
true
end
!~ 运算符与 =~ 相反,如果没有匹配项,则返回 true,如果有匹配项,则返回 false。
有关详细信息,请参阅此博客文章。
评论
:zen === "zen"
我想对运营商进行扩展。===
===
不是相等运算符!
不。
让我们真正了解这一点。
您可能熟悉 Javascript 和 PHP 中的相等运算符,但这不是 Ruby 中的相等运算符,并且具有根本不同的语义。===
那怎么办呢?===
===
是模式匹配运算符!
===
匹配正则表达式===
检查范围成员资格===
检查是否为类的实例===
调用 lambda 表达式===
有时检查是否相等,但大多数情况下不会
那么,这种疯狂是如何理解的呢?
Enumerable#grep
内部使用===
case when
内部使用的语句===
- 有趣的事实,内部使用
rescue
===
这就是为什么您可以在语句中使用正则表达式、类和范围,甚至 lambda 表达式的原因。case when
一些例子
case value
when /regexp/
# value matches this regexp
when 4..10
# value is in range
when MyClass
# value is an instance of class
when ->(value) { ... }
# lambda expression returns true
when a, b, c, d
# value matches one of a through d with `===`
when *array
# value matches an element in array with `===`
when x
# values is equal to x unless x is one of the above
end
所有这些示例也适用于方法。pattern === value
grep
arr = ['the', 'quick', 'brown', 'fox', 1, 1, 2, 3, 5, 8, 13]
arr.grep(/[qx]/)
# => ["quick", "fox"]
arr.grep(4..10)
# => [5, 8]
arr.grep(String)
# => ["the", "quick", "brown", "fox"]
arr.grep(1)
# => [1, 1]
- .eql?- 如果 receiver 和参数具有相同的类型和相等的值,则此运算符返回 true。
例如 - 10.eql?(10.0) 是错误的。
- === - 它将测试 case 语句中的相等性。
例如 - (1...10) === 1 为 true
- == - 此运算符检查两个给定的操作数是否相等。如果等于,则返回 TRUE,否则返回 FALSE。
例如 - (1...10) == 1 为 false
更多例子请点击这里
评论
"a" == "a"
"a" === "a"
"a".eql? "a"
"a".equal? "a"
a = Object.new; b = Object.new
==
===
.equal?
.eql?
true
a
a
a
b