相等之间的区别是什么?, eql ?, ===, and ==?

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

===默认也调用==,它调用equal?…如果这三个方法都没有被覆盖,那么我猜 =====equal?做完全相同的事情?< / p >

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

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

222376 次浏览

我将在这里大量引用Object文档,因为我认为它有一些很好的解释。我鼓励你阅读它,以及这些方法的文档,因为它们在其他类中被重写了,比如字符串

旁注:如果你想自己在不同的对象上尝试这些,可以使用这样的东西:

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}

== -通用的“平等”;

在对象级别,只有当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

请参阅我的答案是,了解case+Regex如何使代码更加简洁。当然,通过提供您自己的===实现,您可以获得自定义的case语义。

eql? - Hash相等

如果objother指向同一个散列键,则eql?方法返回true。Hash使用它来测试成员是否相等。obj2子类通常通过将eql?别名为它们覆盖的==方法来延续这一传统,但也有例外。例如,Numeric类型在==上执行类型转换,而不是在eql?上执行类型转换,因此:

1 == 1.0     #=> true
1.eql? 1.0   #=> false

因此,您可以自由地覆盖它以供自己使用,或者您可以覆盖==并使用alias :eql? :==,以便两个方法的行为相同。

标识比较

==不同,equal?方法永远不应该被子类覆盖:它被用来确定对象的身份(也就是说,a.equal?(b)ab是同一个对象)。

这是有效的指针比较。

=== #——大小写相等

== #——一般相等

两者的工作原理类似,但“===”甚至可以执行case语句

"test" == "test"  #=> true
"test" === "test" #=> true

区别就在这里

String === "test"   #=> true
String == "test"  #=> 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)

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

< p > # EYZ4 < br > 是四个比较器,即。Ruby中比较2个对象的4种方法 在Ruby中,所有比较器(和大多数操作符)实际上都是方法调用,因此您可以自己更改、覆盖和定义这些比较方法的语义。但是,重要的是要理解,当Ruby的内部语言构造使用哪个比较器:

< p > # EYZ1 < br > Ruby在任何地方都使用:==来比较两个对象的,例如。散列值:< / p >
{a: 'z'}  ==  {a: 'Z'}    # => false
{a: 1}    ==  {a: 1.0}    # => true
< p > # EYZ1 < br > Ruby在case/when结构中使用:===。下面的代码段在逻辑上是相同的:

case foo
when bar;  p 'do something'
end


if bar === foo
p 'do something'
end
< p > # EYZ1 < br > 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类集也依赖于哈希键比较。

< p > # EYZ1 < br > Ruby使用:equal?来检查两个对象是否相同。(BasicObject类的)这个方法不应该被覆盖
obj = obj2 = 'a'
obj.equal? obj2       # => true
obj.equal? obj.dup    # => false

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中最严格的平等形式。

< p >的例子: 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吗?

在Hash类中,eql?方法,用于测试键是否相等。需要一些背景知识来解释这一点。在计算的一般上下文中,哈希函数接受任意大小的字符串(或文件),并生成一个称为hashcode的固定大小的字符串或整数,通常称为仅哈希。一些常用的哈希码类型是MD5、SHA-1和CRC。它们被用于加密算法、数据库索引、文件完整性检查等。一些编程语言,如Ruby,提供了一种称为哈希表的集合类型。哈希表是一种类似字典的集合,它成对存储数据,由唯一键及其对应的值组成。在底层,这些密钥以哈希码的形式存储。哈希表通常被称为哈希表。注意单词hashcan是如何指代hashcode或哈希表的。在Ruby编程的上下文中,哈希这个词几乎总是指类似字典的集合。

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

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

Ruby的许多内置类,如String、Range和Regexp,都提供了它们自己的===运算符的实现,也称为case-equal、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吗?方法如果对象是给定类或任何子类的实例,则返回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。

更多信息可在这篇博文

我想详细介绍一下===操作符。

===不是一个相等运算符!

不是。

让我们把这一点讲清楚。

您可能熟悉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 === valuegrep方法。

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]
  1. .eql吗?—如果接收方和实参具有相同类型且值相等,则此操作符返回true。

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

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

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

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

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

更多的例子点击这里