equ?,eql?,===和==之间有什么区别?

den*_*iss 537 ruby comparison equality operators

我试图理解这四种方法之间的区别.我知道默认情况下==调用的方法equal?在两个操作数引用完全相同的对象时返回true.

===默认情况下也调用==哪个调用equal?...好吧,所以如果所有这三个方法都没有被覆盖,那么我猜 ===,==并且equal?做同样的事情?

现在来了eql?.这是做什么的(默认情况下)?它是否调用操作数的hash/id?

为什么Ruby有这么多的平等标志?他们应该在语义上有所不同吗?

jtb*_*des 771

我将在这里大量引用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}
Run Code Online (Sandbox Code Playgroud)

== - 通用"平等"

在Object级别,==仅当objother是同一对象时才返回true .通常,在子类中重写此方法以提供特定于类的含义.

这是最常见的比较,因此是您(作为一个类的作者)决定两个对象是否"相等"的最基本的地方.

=== - 案件平等

对于类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
Run Code Online (Sandbox Code Playgroud)

请参阅我的答案,以获得一个简洁的例子,说明case+ Regex如何使代码更清晰.当然,通过提供自己的===实现,您可以获得自定义case语义.

eql?- Hash平等

eql?如果obj并且other引用相同的散列键,则该方法返回true .这用于Hash测试成员是否相等.对于类的对象Object,eql?是同义词==.子类通常通过eql?对其重写==方法进行别名来继续这种传统,但也有例外.Numeric例如,类型执行跨越==但不跨越的类型转换eql?,因此:

1 == 1.0     #=> true
1.eql? 1.0   #=> false
Run Code Online (Sandbox Code Playgroud)

因此,您可以自由地覆盖它以供自己使用,或者您可以覆盖==和使用,alias :eql? :==因此这两种方法的行为方式相同.

equal? - 身份比较

与此不同==,该equal?方法永远不应被子类覆盖:它用于确定对象标识(即a.equal?(b)iff a与其相同的对象b).

这实际上是指针比较.

  • 我从你的回答中理解,严格是:平等?<eql?<== <===.通常,您使用==.对于某些松散的目的,您使用===.对于严格的情况,你使用eql ?,并且为了完整的身份,你使用相同的? (32认同)
  • 严格的概念没有在文档中强制执行甚至建议,只是"Numeric"以比`=='更严格的方式处理它.这完全取决于班上的作者.`===`在`case`语句之外很少使用. (21认同)
  • 有趣的事实:官方文档现在链接到这个答案(参见http://www.ruby-doc.org/core-2.1.5/String.html#method-i-3D-3D-label-Equality). (7认同)
  • @sawa我通常认为`===`意思是"匹配"(粗略地).如,"正则表达式匹配字符串"或"范围是否匹配(包括)数字". (5认同)
  • ==也就是更大/更小的平等.即,如果你包括Comparable,它将被定义为<=>返回0.这就是1 == 1.0返回true的原因. (4认同)
  • 很好的答案.我没有回答的一个问题是,这些平等中的任何一个是否相互继承.如果我重新定义==,那是否会改变===的行为?等等.一些播放表明===采用==的行为,但是==和eql?不要互相影响. (2认同)

And*_*iep 46

我喜欢jtbandes的答案,但由于它很长,我会添加自己的紧凑答案:

==,===,eql?,equal?
是4个比较器,即 在Ruby中比较2个对象的4种方法.
因为在Ruby中,所有比较器(和大多数运算符)实际上是方法调用,您可以自己更改,覆盖和定义这些比较方法的语义.但是,当Ruby的内部语言构造使用哪个比较器时,重要的是要理解:

==(值比较)
Ruby使用:==无处不在比较2个对象的,例如.哈希值:

{a: 'z'}  ==  {a: 'Z'}    # => false
{a: 1}    ==  {a: 1.0}    # => true
Run Code Online (Sandbox Code Playgroud)

===(案例比较)
Ruby使用:=== in case/when constructs.以下代码段在逻辑上是相同的:

case foo
  when bar;  p 'do something'
end

if bar === foo
  p 'do something'
end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

注意:常用的Ruby类Set也依赖于Hash-key-comparison.

equal?(对象标识比较)
Ruby使用:相等?检查两个对象是否相同.这个方法(类BasicObject)不应该被覆盖.

obj = obj2 = 'a'
obj.equal? obj2       # => true
obj.equal? obj.dup    # => false
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案,但它几乎和jtbandes一样长.:) (28认同)
  • @odigity,大约70%。我可以想到将这30%花在的很多事情。 (2认同)

Bru*_*cca 31

平等运算符:==和!=

==运算符(也称为等于或等于)将在两个对象相等时返回true,否则返回false.

"koan" == "koan" # Output: => true
Run Code Online (Sandbox Code Playgroud)

!=运算符,AKA不等式或bang-tilde与==相反.如果两个对象不相等则返回true,如果它们相等则返回false.

"koan" != "discursive thought" # Output: => true
Run Code Online (Sandbox Code Playgroud)

请注意,具有不同顺序的相同元素的两个数组不相等,同一个字母的大写和小写版本不相等,依此类推.

当比较不同类型的数字(例如,整数和浮点数)时,如果它们的数值相同,则==将返回true.

2 == 2.0 # Output: => true
Run Code Online (Sandbox Code Playgroud)

等于?

与测试两个操作数是否相等的==运算符不同,equal方法检查两个操作数是否引用相同的对象.这是Ruby中最严格的平等形式.

示例:a ="zen"b ="zen"

a.object_id  # Output: => 20139460
b.object_id  # Output :=> 19972120

a.equal? b  # Output: => false
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,我们有两个具有相同值的字符串.但是,它们是两个不同的对象,具有不同的对象ID.因此,平等?方法将返回false.

让我们再试一次,只有这一次b将是对a的引用.请注意,两个变量的对象ID相同,因为它们指向同一个对象.

a = "zen"
b = a

a.object_id  # Output: => 18637360
b.object_id  # Output: => 18637360

a.equal? b  # Output: => true
Run Code Online (Sandbox Code Playgroud)

EQL?

在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
Run Code Online (Sandbox Code Playgroud)

哈希方法在内核模块中实现,包含在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
Run Code Online (Sandbox Code Playgroud)

在Ruby中,当我们将某些东西存储在散列(集合)中时,作为键提供的对象(例如,字符串或符号)被转换为哈希码并存储为哈希码.稍后,当从散列(集合)中检索元素时,我们提供一个对象作为键,将其转换为哈希码并与现有键进行比较.如果匹配,则返回相应项的值.使用eql进行比较?引擎盖下的方法.

"zen".eql? "zen"    # Output: => true
# is the same as
"zen".hash == "zen".hash # Output: => true
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,eql?方法的行为与==方法类似.但是,有一些例外.例如,eql?将整数与float进行比较时,不执行隐式类型转换.

2 == 2.0    # Output: => true
2.eql? 2.0    # Output: => false
2.hash == 2.0.hash  # Output: => false
Run Code Online (Sandbox Code Playgroud)

案例等式运算符:===

许多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
Run Code Online (Sandbox Code Playgroud)

使用可能最适合工作的其他方法可以获得相同的结果.通常,在不牺牲效率和简洁性的情况下,通过尽可能明确地编写易于阅读的代码通常会更好.

2.is_a? Integer   # Output: => true
2.kind_of? Integer  # Output: => true
2.instance_of? Integer # Output: => false
Run Code Online (Sandbox Code Playgroud)

请注意,最后一个示例返回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

范围实现===

当在范围对象上调用===运算符时,如果右侧的值落在左侧的范围内,则返回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
Run Code Online (Sandbox Code Playgroud)

请记住,===运算符调用左侧对象的===方法.所以(1..4)=== 3相当于(1..4).=== 3.换句话说,左操作数的类将定义===方法的哪个实现将是调用,因此操作数位置不可互换.

正则表达式实现===

如果右侧的字符串与左侧的正则表达式匹配,则返回true./ zen/==="今天练习zazen"#输出:=>真#与"今天练习zazen"相同=〜/ zen /

在case/when语句中隐式使用===运算符

在case/when语句中也使用此运算符.这是它最常见的用途.

minutes = 15

case minutes
  when 10..20
    puts "match"
  else
    puts "no match"
end

# Output: match
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,如果Ruby隐式使用了double equal运算符(==),则范围10..20不会被视为等于15之类的整数.它们匹配,因为三元运算符(===)是在所有case/when语句中隐式使用.上例中的代码相当于:

if (10..20) === minutes
  puts "match"
else
  puts "no match"
end
Run Code Online (Sandbox Code Playgroud)

模式匹配运算符:=〜和!〜

=〜(等号)和!〜(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
Run Code Online (Sandbox Code Playgroud)

Regexp类中的实现需要字符串或符号作为参数.

/zen/ =~ "practice zazen"  # Output: => 11
/zen/ =~ "discursive thought" # Output: => nil
Run Code Online (Sandbox Code Playgroud)

在所有实现中,当字符串或符号与Regexp模式匹配时,它返回一个整数,该整数是匹配的位置(索引).如果没有匹配,则返回nil.请记住,在Ruby中,任何整数值都是"truthy",nil是"falsy",因此=〜运算符可以用在if语句和三元运算符中.

puts "yes" if "zazen" =~ /zen/ # Output: => yes
"zazen" =~ /zen/?"yes":"no" # Output: => yes
Run Code Online (Sandbox Code Playgroud)

模式匹配运算符对于编写较短的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
Run Code Online (Sandbox Code Playgroud)

!〜运算符与=〜相反,当没有匹配时返回true,如果匹配则返回false.

更多信息可在此博客文章中找到.

  • 我发现这是一个比目前接受的答案更好的答案,因为它提供了很好的例子,并且对于不同类型的平等意味着什么以及它们存在/使用它们的原因不那么暧昧. (6认同)

小智 9

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.
Run Code Online (Sandbox Code Playgroud)

点击下面的链接继续阅读,它给了我一个清晰的总结理解.

https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/equality-matchers

希望它能帮助别人.


Kis*_*han 8

===#---案例平等

==#---通用平等

两者的作用相似,但"==="甚至做个案陈述

"test" == "test"  #=> true
"test" === "test" #=> true
Run Code Online (Sandbox Code Playgroud)

这里的区别

String === "test"   #=> true
String == "test"  #=> false
Run Code Online (Sandbox Code Playgroud)

  • 他们*的工作方式并不相似,即使当a == b`然后是'a === b`时往往也是如此.但是`a === b`更强大.`===`不是对称的,`a === b`意味着与`b === a`完全不同,更不用说'a == b`了. (3认同)

aku*_*uhn 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
Run Code Online (Sandbox Code Playgroud)

所有这些示例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]
Run Code Online (Sandbox Code Playgroud)