我试图理解这四种方法之间的区别。我默认知道==
调用方法equal?
,当两个操作数引用完全相同的对象时,它返回true。
===
也会调用调用==
的{{1}} ...好吧,如果所有这三种方法都没有被覆盖,那么我猜
equal?
,===
和==
做同样的事情?
现在来equal?
。这是做什么的(默认情况下)?它是否调用操作数的hash / id?
为什么Ruby有这么多的平等标志?他们应该在语义上有所区别吗?
答案 0 :(得分:756)
我将在这里引用the Object documentation,因为我认为它有一些很好的解释。我鼓励您阅读它,以及这些方法的文档,因为它们在其他类中被覆盖,例如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}
==
- 通用“平等”在对象级别,
==
仅在obj
和other
是同一对象时才返回true。通常,在子类中重写此方法以提供特定于类的含义。
这是最常见的比较,因此是你(作为一个类的作者)决定两个对象是否“相等”的最基本的地方。
===
- 案例平等对于类Object,实际上与调用
#==
相同,但通常由后代重写,以在case语句中提供有意义的语义。
这非常有用。具有有趣===
实现的事物的示例:
所以你可以这样做:
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
如何使代码更清晰的简洁示例,请参阅my answer here。当然,通过提供自己的===
实现,您可以获得自定义case
语义。
eql?
- Hash
平等如果
eql?
和obj
引用相同的哈希键,则other
方法返回true。Hash
使用它来测试成员是否相等。 对于班级Object
的对象,eql?
与==
同义。子类通常通过将eql?
别名化为被覆盖的{{1}来继续这一传统方法,但也有例外。例如,==
类型会在Numeric
之间执行类型转换,但不会在==
之间执行,因此:eql?
因此,您可以自由地覆盖它,或者您可以覆盖1 == 1.0 #=> true
1.eql? 1.0 #=> false
并使用==
,这样两种方法的行为方式相同。
alias :eql? :==
- 身份比较与
equal?
不同,==
方法永远不应被子类覆盖:它用于确定对象标识(即equal?
iffa.equal?(b)
是同一个对象作为a
)。
这实际上是指针比较。
答案 1 :(得分:45)
我喜欢jtbandes的回答,但由于它很长,我会加上自己的紧凑答案:
==
,===
,eql?
,equal?
是4个比较器,即。在Ruby中比较2个对象的4种方法
因为在Ruby中,所有比较器(和大多数运算符)实际上是方法调用,您可以自己更改,覆盖和定义这些比较方法的语义。但是,当Ruby的内部语言结构使用哪个比较器时,重要的是要理解
==
(价值比较)
Ruby使用:==无处不在比较2个对象的值,例如。哈希值:
{a: 'z'} == {a: 'Z'} # => false
{a: 1} == {a: 1.0} # => true
===
(案例比较)
Ruby使用:=== in case / when constructs。以下代码段在逻辑上是相同的:
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类Set也依赖于Hash-key-comparison。
equal?
(对象标识比较)
Ruby使用:相等?检查两个对象是否相同。这个方法(类BasicObject)不应该被覆盖。
obj = obj2 = 'a'
obj.equal? obj2 # => true
obj.equal? obj.dup # => false
答案 2 :(得分:29)
==运算符,也称为等于或等于,如果两个对象都相等则返回true,否则返回false。
"koan" == "koan" # Output: => true
!=运算符,也称为不等式,与==相反。如果两个对象不相等则返回true,如果它们相等则返回false。
"koan" != "discursive thought" # Output: => true
请注意,具有不同顺序的相同元素的两个数组不相等,同一个字母的大写和小写版本不相等,依此类推。
当比较不同类型的数字(例如,整数和浮点数)时,如果它们的数值相同,==将返回true。
2 == 2.0 # Output: => true
与测试两个操作数是否相等的==运算符不同,equal方法检查两个操作数是否引用同一个对象。这是Ruby中最严格的平等形式。
实施例: a =" zen" b =" zen"
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
在Hash类中,eql?方法用于测试密钥的相等性。需要一些背景来解释这一点。在计算的一般上下文中,散列函数接受任何大小的字符串(或文件)并生成称为散列码的固定大小的字符串或整数,通常称为散列。一些常用的哈希码类型是MD5,SHA-1和CRC。它们用于加密算法,数据库索引,文件完整性检查等。一些编程语言(如Ruby)提供了一种称为哈希表的集合类型。散列表是类似字典的集合,它们成对存储数据,由唯一键及其对应的值组成。在引擎盖下,这些键存储为哈希码。哈希表通常被称为哈希表。注意hashcan这个词是如何引用哈希码或哈希表的。在Ruby编程的上下文中,单词hash几乎总是指类字典集合。
Ruby提供了一个名为hash的内置方法,用于生成哈希码。在下面的示例中,它接受一个字符串并返回一个哈希码。请注意具有相同值的字符串如何始终具有相同的哈希码,即使它们是不同的对象(具有不同的对象ID)。
"meditation".hash # Output: => 1396080688894079547
"meditation".hash # Output: => 1396080688894079547
"meditation".hash # Output: => 1396080688894079547
哈希方法在内核模块中实现,包含在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)提供了自己的===运算符实现,也称为case-equality,triple equals或threequals。因为它在每个类中的实现方式不同,所以它的行为会有所不同,具体取决于调用它的对象类型。通常,如果右边的对象属于"它返回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?如果对象是给定类或任何子类的实例,则methods返回true。 instance_of方法更严格,只有当对象是该确切类的实例而不是子类时才返回true。
is_a?和kind_of?方法在内核模块中实现,内核模块由Object类混合。两者都是同一方法的别名。我们来验证:
Kernel.instance_method(:kind_of?)== Kernel.instance_method(:is_a?)#Output:=>真
当在范围对象上调用===运算符时,如果右边的值落在左边的范围内,则返回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 / ==="今天练习zazen" #输出:=>真正 # 是相同的 "今天练习zazen" =〜/ zen /
此操作符也用于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
在所有实现中,当字符串或符号与Regexp模式匹配时,它返回一个整数,该整数是匹配的位置(索引)。如果没有匹配,则返回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。
this blog post提供了更多信息。
答案 3 :(得分:8)
两者的作用相似但是" ==="甚至做个案陈述
"test" == "test" #=> true
"test" === "test" #=> true
这里差异
String === "test" #=> true
String == "test" #=> false
答案 4 :(得分:8)
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
希望它可以帮助他人。
答案 5 :(得分:8)
我想扩展===
运算符。
===
不是等式运算符!
不
让我们真正了解这一点。
您可能熟悉===
作为Javascript和PHP中的相等运算符,但这不是Ruby中的相等运算符,并且具有根本不同的语义。
那么===
做了什么?
===
是模式匹配运算符!
===
匹配正则表达式===
检查范围成员资格===
检查是一个类的实例===
调用lambda表达式===
有时会检查相等性,但大部分都不会那么这种疯狂是如何有意义的呢?
Enumerable#grep
在内部使用===
case when
语句在内部使用===
rescue
内部使用===
这就是为什么你可以在case when
语句中使用正则表达式,类和范围甚至lambda表达式。
一些例子
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]
答案 6 :(得分:0)
例如 - 10.eql?(10.0) 是假的。
例如 - (1...10) === 1 为真
例如 - (1...10) == 1 是假的
更多示例click here
答案 7 :(得分:-8)
我为上述所有内容编写了一个简单的测试。
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)