在Ruby中比较对象等价性

6

相关:https://dev59.com/CWkv5IYBdhLWcg3w6lHj - Simon Perepelitsa
@SemyonPerepelitsa:事实证明这并不是相关的,但我很高兴能够回答那个棘手的问题 :-) - Marc-André Lafortune
2个回答

13
在大多数情况下,==eql?的结果相同。但在某些情况下,eql?==更为严格:
42.0 == 42 # => true
42.0.eql?(42) # => false

因此,如果您定义了==,您可能也想定义eql?(或反之亦然)。
选择使用eql?来区分不同键,而不是使用==。当然,它本可以使用==,但eql?更加简洁。
为了避免一直调用eql?,需要计算哈希值,要求两个eql?的对象必须具有相同的哈希值。该哈希值被存储,使得将来的查找非常容易:如果哈希码不匹配,则值不是eql?...
因此,如果您定义了eql?,则必须以明智的方式定义hash
请注意,计算哈希值几乎总是比使用==eql?进行比较更昂贵。但是,一旦计算出哈希值,则检查哈希值是否匹配非常快速。
由于哈希通常涉及大量比较,相对昂贵的哈希计算会针对每个键执行一次,然后再针对每个查找执行一次。想象一下有10个条目的哈希表。在进行第一个查找之前,需要进行10次hash调用来构建它。不过第一个查找将会相对快速:一次hash调用,然后是非常高效的哈希码比较(实际上比这更快,因为它们是“索引的”)。如果有匹配项,则仍须调用eql?以确保它是真正的匹配。事实上,两个对象可能不是eql?但具有相同的哈希值。唯一的保证是两个eql?的对象必须具有相同的哈希,但两个不同的对象也可能具有相同的哈希。

如果您想使用Array完成相同的操作,则每次查找可能需要10次eql?调用。

对于这个问题,我认为你提供的 Ruby 入门教程并不是很清晰。它忽略了计算哈希值可能会很耗时这一事实,因此只有在有好的假设时才进行计算,即当每个元素将被比较多次时。此外,它给出的自定义 eql? 示例使用 == 来比较实例变量,这是令人遗憾的。理想情况下,它应该使用 eql? 来保持一致,就像数组如果其元素 == 则数组也 ==,如果其元素 eql? 则数组也 eql? 一样。最后,它真的应该提到 Struct,它为你定义了相当不错的 ==hasheql?

感谢您在这里提供的详细解释,这确实有助于澄清问题。 - Ricky

3
例如,Array#hash说:
两个包含相同内容的数组将具有相同的哈希码(并且将使用eql?进行比较)。
Array#==则表示:
相等 - 如果两个数组包含相同数量的元素,并且每个元素都等于(根据Object#==)other_ary中对应的元素,则它们是相等的。
Array#eql?则表示:
如果self和other是相同的对象,或者都是包含相同内容的数组,则返回true。
因此,根据文档,很明显eql?更快,因为它使用hash值。而#==则执行两个操作 -
1. 数组的长度 2. 每个元素的等价性测试。

那么为什么不使用“==”而不是“eql”进行比较呢?是因为“eql”使用哈希码进行快速比较吗?它不是内部必须使用“==”来比较两个哈希码的等价性吗? - Ricky
2
这个答案是错误的:eql?不使用hash值,也不比==更快。 - Marc-André Lafortune
是的,它们的源代码几乎完全相同。(您可以通过点击文档查看。)我想区别在于==如果需要(通过调用to_ary方法)可以将其他对象转换为数组,而eql?则强制其必须是Array类。 - Simon Perepelitsa
哦,他们使用各自的方法来比较元素。 - Simon Perepelitsa
@SemyonPerepelitsa:确实。我已经尝试在https://github.com/ruby/ruby/commit/017f0ffe1822264a8ead114f0b6055805185c0ac中将其更加明确化。 - Marc-André Lafortune

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接