ruby equal?、eql?、=== 和 == 之间有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7156955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:16:36  来源:igfitidea点击:

What's the difference between equal?, eql?, ===, and ==?

rubycomparisonoperatorsequality

提问by denniss

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.

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

===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?

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

Now comes eql?. What does this do (by default)? Does it make a call to the operand's hash/id?

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

Why does Ruby have so many equality signs? Are they supposed to differ in semantics?

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

回答by jtbandes

I'm going to heavily quote the Object documentationhere, 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.

我将在这里大量引用Object 文档,因为我认为它有一些很好的解释。我鼓励您阅读它,以及这些方法的文档,因为它们在其他类中被覆盖,比如String

Side note: if you want to try these out for yourself on different objects, use something like this:

旁注:如果您想在不同的对象上亲自尝试这些,请使用以下内容:

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}


==— generic "equality"

==— 通用的“平等”

At the Object level, ==returns true only if objand otherare the same object. Typically, this method is overridden in descendant classes to provide class-specific meaning.

在对象级别,==仅当objother是同一个对象时才返回 true 。通常,此方法在后代类中被覆盖以提供特定于类的含义。

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 equality

===— 大小写相等

For class Object, effectively the same as calling #==, but typically overridden by descendants to provide meaningful semantics in case statements.

对于类 Object,实际上与调用相同#==,但通常由后代覆盖以在 case 语句中提供有意义的语义。

This is incredibly useful. Examples of things which have interesting ===implementations:

这非常有用。具有有趣===实现的事物示例:

  • Range
  • Regex
  • Proc (in Ruby 1.9)
  • 范围
  • 正则表达式
  • 进程(在 Ruby 1.9 中)

So you can do things like:

因此,您可以执行以下操作:

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

See my answer herefor a neat example of how case+Regexcan make code a lot cleaner. And of course, by providing your own ===implementation, you can get custom casesemantics.

有关+如何使代码更清晰的简洁示例,请参阅我的答案。当然,通过提供您自己的实现,您可以获得自定义语义。caseRegex===case

eql?Hashequality

eql?Hash平等

The eql?method returns true if objand otherrefer to the same hash key. This is used by Hashto test members for equality. For objects of class Object, eql?is synonymous with ==.Subclasses normally continue this tradition by aliasing eql?to their overridden ==method, but there are exceptions. Numerictypes, for example, perform type conversion across ==, but not across eql?, so:

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

eql?如果objother引用相同的哈希键,则该方法返回 true 。这用于Hash测试成员是否相等。对于类的对象Objecteql?是同义词==子类通常通过eql?为其覆盖的==方法设置别名来延续这一传统,但也有例外。Numeric类型,例如,执行类型转换 cross ==,但不执行 cross eql?,所以:

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

So you're free to override this for your own uses, or you can override ==and use alias :eql? :==so the two methods behave the same way.

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

equal?— identity comparison

equal?— 身份比较

Unlike ==, the equal?method should never be overridden by subclasses: it is used to determine object identity (that is, a.equal?(b)iff ais the same object as b).

与 不同==,该equal?方法不应被子类覆盖:它用于确定对象身份(即,a.equal?(b)iff 与a是同一对象b)。

This is effectively pointer comparison.

这实际上是指针比较。

回答by Andreas Rayo Kniep

I love jtbandes answer, but since it is pretty long, I will add my own compact answer:

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

==, ===, 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:

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

==(value comparison)
Ruby uses :== everywhere to compare the valuesof 2 objects, eg. Hash-values:

==(值比较)
Ruby 到处都使用 :== 来比较2 个对象的,例如。哈希值:

{a: 'z'}  ==  {a: 'Z'}    # => false
{a: 1}    ==  {a: 1.0}    # => true

===(case comparison)
Ruby uses :=== in case/when constructs. The following code snippets are logically identical:

===(案例比较)
Ruby 在 case/when 构造中使用 :=== 。以下代码片段在逻辑上是相同的:

case foo
  when bar;  p 'do something'
end

if bar === foo
  p 'do something'
end

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:

eql?(哈希键比较)
Ruby 使用 :eql? (结合hash方法)比较Hash-keys。在大多数课程中 :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

Note: The commonly used Ruby-class Set also relies on Hash-key-comparison.

注意:常用的 Ruby-class Set 也依赖于 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.

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

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

回答by BrunoFacca

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.

== 运算符,也称为相等或双相等,如果两个对象相等,则返回 true,否则返回 false。

"koan" == "koan" # Output: => true

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.

!= 运算符,也称为不等式,与 == 相反。如果两个对象不相等,则返回 true,如果相等则返回 false。

"koan" != "discursive thought" # Output: => true

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.

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

2 == 2.0 # Output: => 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.

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

Example: a = "zen" b = "zen"

示例:a = "禅" b = "禅"

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

a.equal? b  # Output: => false

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.

在上面的示例中,我们有两个具有相同值的字符串。但是,它们是两个不同的对象,具有不同的对象 ID。因此,平等?方法将返回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.

让我们再试一次,只是这次 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.

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

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).

Ruby 提供了一种称为 hash 的内置方法来生成哈希码。在下面的示例中,它接受一个字符串并返回一个哈希码。请注意具有相同值的字符串如何始终具有相同的哈希码,即使它们是不同的对象(具有不同的对象 ID)。

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

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.

hash 方法在 Kernel 模块中实现,包含在 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

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.

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

"zen".eql? "zen"    # Output: => true
# is the same as
"zen".hash == "zen".hash # Output: => true

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.

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

2 == 2.0    # Output: => true
2.eql? 2.0    # Output: => false
2.hash == 2.0.hash  # Output: => false

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).

许多 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

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.

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

2.is_a? Integer   # Output: => true
2.kind_of? Integer  # Output: => true
2.instance_of? Integer # Output: => false

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.

请注意,最后一个示例返回 false,因为诸如 2 之类的整数是 Fixnum 类的实例,它是 Integer 类的子类。===, is_a? 和instance_of?如果对象是给定类或任何子类的实例,则方法返回 true。instance_of 方法更严格,仅当对象是该类的实例而不是子类时才返回 true。

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:

is_a?和种类?方法在 Kernel 模块中实现,由 Object 类混入。两者都是同一方法的别名。我们来验证一下:

Kernel.instance_method(:kind_of?) == Kernel.instance_method(:is_a?) # Output: => true

Kernel.instance_method(:kind_of?) == Kernel.instance_method(:is_a?) # 输出:=> 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.

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

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.

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

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/

如果右侧的字符串与左侧的正则表达式匹配,则返回 true。/zen/ === "practice zazen today" # 输出:=> true # 和 "practice zazen today"=~ /zen/

Implicit usage of the === operator on case/when statements

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

This operator is also used under the hood on case/when statements. That is its most common use.

此运算符也用于 case/when 语句的幕后。这是它最常见的用途。

minutes = 15

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

# Output: match

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:

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

if (10..20) === minutes
  puts "match"
else
  puts "no match"
end

Pattern matching operators: =~ and !~

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

The =~ (equal-tilde) and !~ (bang-tilde) operators are used to match strings and symbols against regex patterns.

=~ (equal-tilde) 和 !~ (bang-tilde) 运算符用于根据正则表达式模式匹配字符串和符号。

The implementation of the =~ method in the String and Symbol classes expects a regular expression (an instance of the Regexp class) as an argument.

String 和 Symbol 类中 =~ 方法的实现需要一个正则表达式(Regexp 类的一个实例)作为参数。

"practice zazen" =~ /zen/   # Output: => 11
"practice zazen" =~ /discursive thought/ # Output: => nil

:zazen =~ /zen/    # Output: => 2
:zazen =~ /discursive thought/  # Output: => nil

The implementation in the Regexp class expects a string or a symbol as an argument.

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

/zen/ =~ "practice zazen"  # Output: => 11
/zen/ =~ "discursive thought" # Output: => nil

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.

在所有实现中,当字符串或符号匹配 Regexp 模式时,它返回一个整数,它是匹配的位置(索引)。如果没有匹配项,则返回 nil。请记住,在 Ruby 中,任何整数值都是“真”而 nil 是“假”,因此 =~ 运算符可用于 if 语句和三元运算符。

puts "yes" if "zazen" =~ /zen/ # Output: => yes
"zazen" =~ /zen/?"yes":"no" # Output: => yes

Pattern-matching operators are also useful for writing shorter if statements. Example:

模式匹配运算符对于编写较短的 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

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.

这篇博文提供了更多信息。

回答by kalibbala

Ruby exposes several different methods for handling equality:

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.

Continue reading by clicking the link below, it gave me a clear summarized understanding.

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

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

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

Hope it helps others.

希望它可以帮助其他人。

回答by Kishore Mohan

=== #---case equality

=== #---大小写相等

== #--- generic equality

== #--- 泛型相等

both works similar but "===" even do case statements

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

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

here the difference

这里的区别

String === "test"   #=> true
String == "test"  #=> false

回答by akuhn

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.

您可能熟悉===Javascript 和 PHP 中的相等运算符,但这不是 Ruby 中的相等运算符,并且具有根本不同的语义。

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 not
  • ===匹配正则表达式
  • ===检查范围成员资格
  • ===检查是一个类的实例
  • ===调用 lambda 表达式
  • ===有时会检查相等性,但大多数情况下不会

So how does this madness make sense?

那么这种疯狂有什么意义呢?

  • Enumerable#grepuses ===internally
  • case whenstatements use ===internally
  • Fun fact, rescueuses ===internally
  • Enumerable#grep===内部使用
  • case when语句在===内部使用
  • 有趣的事实,内部rescue使用===

That is why you can use regular expressions and classes and ranges and even lambda expressions in a case whenstatement.

这就是为什么您可以在case when语句中使用正则表达式、类和范围甚至 lambda 表达式的原因。

Some examples

一些例子

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

All these example work with pattern === valuetoo, as well as with grepmethod.

所有这些示例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]

回答by Tom Phan

I wrote a simple test for all the above.

我为上述所有内容编写了一个简单的测试。

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)