我试图理解这四种方法之间的区别。我知道默认情况下==调用方法等于?当两个操作数都指向同一个对象时返回true。

===默认也调用==哪个调用equal?…如果这三个方法都没有被覆盖,那么我猜 ===, == and equal?做完全一样的事情?

现在是eql?。这是做什么(默认情况下)?它是否调用操作数的哈希/id?

为什么Ruby有这么多等号?它们应该在语义上有所不同吗?


当前回答

我喜欢jtband的答案,但因为它太长了,我将添加我自己的紧凑答案:

==, ===, eql?,等于多少? 是四个比较器,即。Ruby中比较两个对象的4种方法。 在Ruby中,所有比较器(和大多数操作符)实际上都是方法调用,因此您可以自己更改、覆盖和定义这些比较方法的语义。然而,重要的是要理解,当Ruby的内部语言构造使用哪个比较器:

==(价值比较) Ruby在任何地方都使用:==来比较两个对象的值,例如。散列值:

{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类集也依赖于哈希键比较。

等于多少?(对象身份比较) Ruby使用:equal?来检查两个对象是否相同。(BasicObject类的)这个方法不应该被覆盖。

obj = obj2 = 'a'
obj.equal? obj2       # => true
obj.equal? obj.dup    # => false

其他回答

我将在这里大量引用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}

== -一般的"平等"

在Object级别,==仅当obj和other是同一个对象时才返回true。通常,这个方法在子类中被重写,以提供特定于类的含义。

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

=== - case相等

对于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如何使代码更简洁。当然,通过提供您自己的===实现,您可以获得自定义大小写语义。

eql吗?-哈希等价

eql吗?如果obj和other指向相同的散列键,则方法返回true。Hash使用它来测试成员是否相等。对于Object类的对象,eql?是==的同义词。子类通常通过别名eql?它们被重写的==方法,但也有例外。例如,数值类型跨==执行类型转换,但不跨eql?,所以: 1 == 1.0 #=> true 1. eql ?1.0 #=> false

因此,您可以自由地为自己的用途覆盖它,或者您可以覆盖==并使用别名:eql?:==所以这两个方法的行为是相同的。

等于多少?-身份比较

不像==,相等?方法永远不应该被子类覆盖:它用于确定对象的身份(即a.equal?(b)如果a与b是相同的对象)。

这是有效的指针比较。

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

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

当比较不同类型的数字(例如,integer和float)时,如果它们的数值相同,==将返回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

eql?

In the Hash class, the eql? method it is used to test keys for equality. Some background is required to explain this. In the general context of computing, a hash function takes a string (or a file) of any size and generates a string or integer of fixed size called hashcode, commonly referred to as only hash. Some commonly used hashcode types are MD5, SHA-1, and CRC. They are used in encryption algorithms, database indexing, file integrity checking, etc. Some programming languages, such as Ruby, provide a collection type called hash table. Hash tables are dictionary-like collections which store data in pairs, consisting of unique keys and their corresponding values. Under the hood, those keys are stored as hashcodes. Hash tables are commonly referred to as just hashes. Notice how the word hashcan refer to a hashcode or to a hash table. In the context of Ruby programming, the word hash almost always refers to the dictionary-like collection.

Ruby提供了一种称为hash的内置方法来生成hashcode。在下面的例子中,它接受一个字符串并返回一个hashcode。请注意具有相同值的字符串总是具有相同的hashcode,即使它们是不同的对象(具有不同的对象id)。

"meditation".hash  # Output: => 1396080688894079547
"meditation".hash  # Output: => 1396080688894079547
"meditation".hash  # Output: => 1396080688894079547

哈希方法是在Kernel模块中实现的,包含在Object类中,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中,当我们在哈希(集合)中存储一些东西时,作为键提供的对象(例如,字符串或符号)将被转换为哈希代码并存储为哈希代码。稍后,当从散列(集合)中检索元素时,我们提供一个对象作为键,该键被转换为hashcode并与现有键进行比较。如果匹配,则返回对应项的值。使用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

大小写相等运算符:===

Many of Ruby's built-in classes, such as String, Range, and Regexp, provide their own implementations of the === operator, also known as case-equality, triple equals or threequals. Because it's implemented differently in each class, it will behave differently depending on the type of object it was called on. Generally, it returns true if the object on the right "belongs to" or "is a member of" the object on the left. For instance, it can be used to test if an object is an instance of a class (or one of its subclasses).

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

Range ===的实现

当对范围对象调用===运算符时,如果右边的值落在左边的范围内,则返回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/ === "practice zazen today" #输出:=> 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

模式匹配操作符:=~和

=~ (equal-tilde)和!~ (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中,任何整数值都是“真值”,nil是“假值”,所以=~操作符可以用在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。

更多信息可以在这篇博客文章中找到。

我编写了一个简单的测试。

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)

.eql吗?—如果接收方和实参具有相同类型且值相等,则此操作符返回true。

例如- 10.eql?(10.0)为假值。

=== -它将在case语句中测试是否相等。

例如-(1…10)=== 1为真

== -该操作符检查两个给定的操作数是否相等。如果等于,则返回TRUE,否则返回FALSE。

例如-(1…10)== 1为假

更多示例请点击这里