equal?、eql?、=== 和 == 之间有什么区别?
我试图理解这四种方法之间的区别。我知道默认情况下 ==
调用方法 equal?
当两个操作数引用完全相同的对象时该方法返回 true。
===
默认情况下也会调用 ==
,后者调用 equal?
...好吧,所以如果这三个方法都没有被重写,那么我猜 ===
、==
和 equal?
做的事情完全相同吗?
现在出现了eql?
。这是做什么的(默认情况下)?它是否调用操作数的哈希/ID?
为什么 Ruby 有这么多等号?它们在语义上应该有所不同吗?
I am trying to understand the difference between these four methods. I know by default that ==
calls the method equal?
which returns true when both operands refer to exactly the same object.
===
by default also calls ==
which calls equal?
... okay, so if all these three methods are not overridden, then I guess===
, ==
and equal?
do exactly the same thing?
Now comes eql?
. What does this do (by default)? Does it make a call to the operand's hash/id?
Why does Ruby have so many equality signs? Are they supposed to differ in semantics?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
相等运算符:== 和 !=
== 运算符也称为相等或双重相等,如果两个对象相等则返回 true,否则返回 false。
!= 运算符也称为不等式,与 == 相反。如果两个对象不相等,则返回 true;如果相等,则返回 false。
请注意,具有相同元素但顺序不同的两个数组不相等,同一字母的大写和小写版本不相等等等。
当比较不同类型的数字(例如整数和浮点数)时,如果它们的数值相同,==将返回true。
平等的?
与测试两个操作数是否相等的 == 运算符不同,equal 方法检查两个操作数是否引用同一个对象。这是 Ruby 中最严格的平等形式。
例子:
一个=“禅宗”
b = "zen"
在上面的示例中,我们有两个具有相同值的字符串。但是,它们是两个不同的对象,具有不同的对象 ID。那么,平等呢?方法将返回 false。
我们再试一次,只是这次 b 将成为对 a 的引用。请注意,两个变量的对象 ID 相同,因为它们指向同一个对象。
情商?
在 Hash 类中,eql?方法用于测试键是否相等。需要一些背景知识来解释这一点。在一般的计算环境中,哈希函数接受任意大小的字符串(或文件)并生成固定大小的字符串或整数,称为哈希码,通常称为唯一哈希。一些常用的哈希码类型包括 MD5、SHA-1 和 CRC。它们用于加密算法、数据库索引、文件完整性检查等。一些编程语言(例如Ruby)提供称为哈希表的集合类型。哈希表是类似字典的集合,成对存储数据,由唯一键及其对应的值组成。在底层,这些密钥存储为哈希码。哈希表通常简称为哈希。请注意“哈希”一词如何指代哈希码或哈希表。在 Ruby 编程的上下文中,哈希这个词几乎总是指类似字典的集合。
Ruby 提供了一个名为 hash 的内置方法来生成哈希码。在下面的示例中,它接受一个字符串并返回一个哈希码。请注意,具有相同值的字符串始终具有相同的哈希码,即使它们是不同的对象(具有不同的对象 ID)。
hash 方法在 Kernel 模块中实现,包含在 Object 类中,该类是所有 Ruby 对象的默认根。一些类(例如 Symbol 和 Integer)使用默认实现,其他类(例如 String 和 Hash)提供自己的实现。
在 Ruby 中,当我们将某些内容存储在哈希(集合)中时,作为键提供的对象(例如字符串或符号)将被转换为哈希码并存储。稍后,当从哈希(集合)中检索元素时,我们提供一个对象作为键,该对象被转换为哈希码并与现有键进行比较。如果匹配,则返回相应项的值。使用 eql? 进行比较方法在幕后。
在大多数情况下,eql?方法的行为与 == 方法类似。然而,也有一些例外。例如,eql?将整数与浮点数进行比较时,不执行隐式类型转换。
大小写相等运算符:===
许多 Ruby 的内置类(例如 String、Range 和 Regexp)都提供了自己的 === 运算符实现,也称为大小写相等、三重等于或三等式。由于它在每个类中的实现方式不同,因此根据调用它的对象类型,它的行为也会有所不同。一般来说,如果右侧的对象“属于”左侧的对象或“是其成员”,则返回 true。例如,它可用于测试对象是否是类(或其子类之一)的实例。
使用可能最适合该工作的其他方法也可以实现相同的结果。通常最好是在不牺牲效率和简洁性的情况下,尽可能明确地编写易于阅读的代码。
请注意,最后一个示例返回 false,因为诸如 2 之类的整数是 Fixnum 类的实例,而 Fixnum 类是 Integer 类的子类。 ===,is_a?和instance_of?如果对象是给定类或任何子类的实例,方法将返回 true。 instance_of 方法更严格,仅当对象是该确切类的实例而不是子类时才返回 true。
is_a?和种类?方法在 Kernel 模块中实现,并由 Object 类混合。两者都是同一方法的别名。让我们验证一下:
Kernel.instance_method(:kind_of?) == Kernel.instance_method(:is_a?) # 输出:=>; true
Range === 的实现
当对 range 对象调用 === 运算符时,如果右侧的值落在左侧的范围内,则返回 true。
请记住, === 运算符调用左侧对象的 === 方法。因此 (1..4) === 3 等价于 (1..4).=== 3。换句话说,左侧操作数的类将定义 === 方法的实现被调用,因此操作数位置不可互换。
=== 的 Regexp 实现
如果右侧的字符串与左侧的正则表达式匹配,则返回 true。
/zen/ === "今天练习坐禅" # 输出: =>真的
# 与
"practice zazen Today"=~ /zen/
在 case/when 语句上隐式使用 === 运算符
该运算符也在 case/when 语句的幕后使用。这是它最常见的用途。
在上面的示例中,如果 Ruby 隐式使用了双等号运算符 (==),则范围 10..20 将不会被视为等于整数,例如 15。它们匹配是因为三等号运算符 (===) 是在所有 case/when 语句中隐式使用。上面示例中的代码相当于:
模式匹配运算符:=~ 和 !~
=~(等代号)和 !~(bang-代号)运算符用于将字符串和符号与正则表达式模式进行匹配。
String 和 Symbol 类中 =~ 方法的实现需要一个正则表达式(Regexp 类的实例)作为参数。
Regexp 类中的实现需要一个字符串或符号作为参数。
在所有实现中,当字符串或符号与正则表达式模式匹配时,它返回一个整数,即匹配的位置(索引)。如果没有匹配,则返回 nil。请记住,在 Ruby 中,任何整数值都是“真”,而 nil 是“假”,因此 =~ 运算符可以在 if 语句和三元运算符中使用。
模式匹配运算符对于编写较短的 if 语句也很有用。示例:
!~ 运算符与 =~ 相反,如果不匹配则返回 true,如果匹配则返回 false。
如需了解更多信息,请访问此博文。
Equality operators: == and !=
The == operator, also known as equality or double equal, will return true if both objects are equal and false if they are not.
The != operator, also known as inequality, is the opposite of ==. It will return true if both objects are not equal and false if they are equal.
Note that two arrays with the same elements in a different order are not equal, uppercase and lowercase versions of the same letter are not equal and so on.
When comparing numbers of different types (e.g., integer and float), if their numeric value is the same, == will return true.
equal?
Unlike the == operator which tests if both operands are equal, the equal method checks if the two operands refer to the same object. This is the strictest form of equality in Ruby.
Example:
a = "zen"
b = "zen"
In the example above, we have two strings with the same value. However, they are two distinct objects, with different object IDs. Hence, the equal? method will return false.
Let's try again, only this time b will be a reference to a. Notice that the object ID is the same for both variables, as they point to the same object.
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 provides a built-in method called hash for generating hashcodes. In the example below, it takes a string and returns a hashcode. Notice how strings with the same value always have the same hashcode, even though they are distinct objects (with different object IDs).
The hash method is implemented in the Kernel module, included in the Object class, which is the default root of all Ruby objects. Some classes such as Symbol and Integer use the default implementation, others like String and Hash provide their own implementations.
In Ruby, when we store something in a hash (collection), the object provided as a key (e.g., string or symbol) is converted into and stored as a hashcode. Later, when retrieving an element from the hash (collection), we provide an object as a key, which is converted into a hashcode and compared to the existing keys. If there is a match, the value of the corresponding item is returned. The comparison is made using the eql? method under the hood.
In most cases, the eql? method behaves similarly to the == method. However, there are a few exceptions. For instance, eql? does not perform implicit type conversion when comparing an integer to a float.
Case equality operator: ===
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).
The same result can be achieved with other methods which are probably best suited for the job. It's usually better to write code that is easy to read by being as explicit as possible, without sacrificing efficiency and conciseness.
Notice the last example returned false because integers such as 2 are instances of the Fixnum class, which is a subclass of the Integer class. The ===, is_a? and instance_of? methods return true if the object is an instance of the given class or any subclasses. The instance_of method is stricter and only returns true if the object is an instance of that exact class, not a subclass.
The is_a? and kind_of? methods are implemented in the Kernel module, which is mixed in by the Object class. Both are aliases to the same method. Let's verify:
Kernel.instance_method(:kind_of?) == Kernel.instance_method(:is_a?) # Output: => true
Range Implementation of ===
When the === operator is called on a range object, it returns true if the value on the right falls within the range on the left.
Remember that the === operator invokes the === method of the left-hand object. So (1..4) === 3 is equivalent to (1..4).=== 3. In other words, the class of the left-hand operand will define which implementation of the === method will be called, so the operand positions are not interchangeable.
Regexp Implementation of ===
Returns true if the string on the right matches the regular expression on the left.
/zen/ === "practice zazen today" # Output: => true
# is the same as
"practice zazen today"=~ /zen/
Implicit usage of the === operator on case/when statements
This operator is also used under the hood on case/when statements. That is its most common use.
In the example above, if Ruby had implicitly used the double equal operator (==), the range 10..20 would not be considered equal to an integer such as 15. They match because the triple equal operator (===) is implicitly used in all case/when statements. The code in the example above is equivalent to:
Pattern matching operators: =~ and !~
The =~ (equal-tilde) and !~ (bang-tilde) operators are used to match strings and symbols against regex patterns.
The implementation of the =~ method in the String and Symbol classes expects a regular expression (an instance of the Regexp class) as an argument.
The implementation in the Regexp class expects a string or a symbol as an argument.
In all implementations, when the string or symbol matches the Regexp pattern, it returns an integer which is the position (index) of the match. If there is no match, it returns nil. Remember that, in Ruby, any integer value is "truthy" and nil is "falsy", so the =~ operator can be used in if statements and ternary operators.
Pattern-matching operators are also useful for writing shorter if statements. Example:
The !~ operator is the opposite of =~, it returns true when there is no match and false if there is a match.
More info is available at this blog post.
我想扩展
===
运算符。===
不是相等运算符!不是。
让我们真正了解这一点。
您可能熟悉
===
作为 Javascript 和 PHP 中的相等运算符,但这并不是 Ruby 中的相等运算符,并且具有根本不同的语义。那么
===
是做什么的呢?===
是模式匹配运算符!===
匹配正则表达式===
检查范围成员资格===
检查是否是类的实例=== 调用 lambda 表达式
===
有时会检查相等性,但大多数情况下不会那么这种疯狂的行为有什么意义呢?
Enumerable#grep
在内部使用===
case when
语句在内部使用===
rescue
在内部使用===
这就是为什么您可以在
case when
语句中使用正则表达式、类和范围,甚至 lambda 表达式。一些示例
所有这些示例都适用于
pattern === value
以及grep
方法。I would like to expand on the
===
operator.===
is not an equality operator!Not.
Let's get that point really across.
You might be familiar with
===
as an equality operator in Javascript and PHP, but this just not an equality operator in Ruby and has fundamentally different semantics.So what does
===
do?===
is the pattern matching operator!===
matches regular expressions===
checks range membership===
checks being instance of a class===
calls lambda expressions===
sometimes checks equality, but mostly it does notSo how does this madness make sense?
Enumerable#grep
uses===
internallycase when
statements use===
internallyrescue
uses===
internallyThat is why you can use regular expressions and classes and ranges and even lambda expressions in a
case when
statement.Some examples
All these example work with
pattern === value
too, as well as withgrep
method.Ruby 公开了几种不同的处理相等的方法:
点击下面的链接继续阅读,它让我有了一个清晰的总结性的理解。
希望对其他人有帮助。
Ruby exposes several different methods for handling equality:
Continue reading by clicking the link below, it gave me a clear summarized understanding.
Hope it helps others.
=== #---case equals
== #--- generic equals
两者的工作原理相似,但“===”甚至 case 语句
这里是区别
=== #---case equality
== #--- generic equality
both works similar but "===" even do case statements
here the difference
例如 - 10.eql?(10.0) 为假。
例如 - (1...10) === 1 is true
例如 - (1...10) == 1 为 false
了解更多示例 点击此处
for example - 10.eql?(10.0) is false.
for example - (1...10) === 1 is true
for example - (1...10) == 1 is false
for more example click here
我为上述所有内容编写了一个简单的测试。
I wrote a simple test for all the above.
我将在这里大量引用对象文档 ,因为我认为它有一些很好的解释。我鼓励您阅读它,以及这些方法的文档,因为它们在其他类中被覆盖,例如 字符串。
旁注:如果您想在不同的对象上亲自尝试这些,请使用如下内容:
==
— 通用“相等”这是最常见的比较,因此也是您(作为类的作者)决定两个对象是否“相等”的最基本的地方。
===
— 大小写相等这非常有用。具有有趣的
===
实现的示例:因此您可以执行以下操作:
请参阅 我的回答在这里,这是一个很好的例子
case
+Regex
可以使代码更加简洁。当然,通过提供您自己的===
实现,您可以获得自定义case
语义。eql?
—哈希
相等性因此您可以随意覆盖此设置供您自己使用,或者您可以覆盖
==
并使用alias :eql? :==
所以这两个方法的行为方式相同。equal?
— 身份比较这实际上是指针比较。
I'm going to heavily quote the Object documentation here, because I think it has some great explanations. I encourage you to read it, and also the documentation for these methods as they're overridden in other classes, like String.
Side note: if you want to try these out for yourself on different objects, use something like this:
==
— generic "equality"This is the most common comparison, and thus the most fundamental place where you (as the author of a class) get to decide if two objects are "equal" or not.
===
— case equalityThis is incredibly useful. Examples of things which have interesting
===
implementations:So you can do things like:
See my answer here for a neat example of how
case
+Regex
can make code a lot cleaner. And of course, by providing your own===
implementation, you can get customcase
semantics.eql?
—Hash
equalitySo you're free to override this for your own uses, or you can override
==
and usealias :eql? :==
so the two methods behave the same way.equal?
— identity comparisonThis is effectively pointer comparison.
我喜欢 jtbandes 答案,但由于它很长,我将添加我自己的紧凑答案:
==
,===
,eql?< /code>,
等于?
是 4 个比较器,即在 Ruby 中比较 2 个对象的 4 种方法。
由于在 Ruby 中,所有比较器(以及大多数运算符)实际上都是方法调用,因此您可以自己更改、覆盖和定义这些比较方法的语义。然而,重要的是要了解,Ruby 的内部语言构造何时使用哪个比较器:
==
(值比较)Ruby 在任何地方都使用 :== 来比较 2 个对象的值,例如。哈希值:
===
(大小写比较)Ruby 在 case/when 构造中使用 :=== 。以下代码片段在逻辑上是相同的:
eql?
(哈希键比较)Ruby 使用 :eql? (与哈希方法结合)比较哈希键。在大多数课程中:eql?与 :== 相同。
关于 :eql? 的知识?仅当您想要创建自己的特殊类时才重要:
注意:常用的 Ruby 类集也依赖于哈希键比较。
等于?
(对象身份比较)Ruby 使用 :equal?检查两个对象是否相同。该方法(BasicObject 类)不应被覆盖。
I love jtbandes answer, but since it is pretty long, I will add my own compact answer:
==
,===
,eql?
,equal?
are 4 comparators, ie. 4 ways to compare 2 objects, in Ruby.
As, in Ruby, all comparators (and most operators) are actually method-calls, you can change, overwrite, and define the semantics of these comparing methods yourself. However, it is important to understand, when Ruby's internal language constructs use which comparator:
==
(value comparison)Ruby uses :== everywhere to compare the values of 2 objects, eg. Hash-values:
===
(case comparison)Ruby uses :=== in case/when constructs. The following code snippets are logically identical:
eql?
(Hash-key comparison)Ruby uses :eql? (in combination with the method hash) to compare Hash-keys. In most classes :eql? is identical with :==.
Knowledge about :eql? is only important, when you want to create your own special classes:
Note: The commonly used Ruby-class Set also relies on Hash-key-comparison.
equal?
(object identity comparison)Ruby uses :equal? to check if two objects are identical. This method (of class BasicObject) is not supposed to be overwritten.