ruby - Ruby `uniq` 方法用于相等性检查是什么?
问题描述
我对在 Ruby 中的对象数组中实现自定义相等方法很感兴趣。这是一个精简的示例:
class Foo
attr_accessor :a, :b
def initialize(a, b)
@a = a
@b = b
end
def ==(other)
puts 'doing comparison'
@a == @a && @b == @b
end
def to_s
"#{@a}: #{@b}"
end
end
a = [
Foo.new(1, 1),
Foo.new(1, 2),
Foo.new(2, 1),
Foo.new(2, 2),
Foo.new(2, 2)
]
a.uniq
我希望 uniq 方法调用Foo#==
,并删除 Foo 的最后一个实例。相反,我没有看到“进行比较”调试行,并且数组的长度保持不变。
笔记:
- 我正在使用红宝石 2.2.2
- 我尝试将方法定义为
===
- 我已经用 . 长期完成了它
a.uniq{|x| [x.a, x.b]}
,但我不喜欢这个解决方案,它使代码看起来很混乱。
解决方案
它使用它们的哈希和eql比较值?效率的方法。
https://ruby-doc.org/core-2.5.0/Array.html#method-i-uniq-3F
所以你应该覆盖eql?
(即==
)和hash
更新:
我无法完全解释为什么会这样,但是压倒一切hash
并且==
不起作用。我想这uniq
是在 C 中实现的原因:
来自:array.c(C方法):所有者:数组可见性:公共行数:20
static VALUE
rb_ary_uniq(VALUE ary)
{
VALUE hash, uniq;
if (RARRAY_LEN(ary) <= 1)
return rb_ary_dup(ary);
if (rb_block_given_p()) {
hash = ary_make_hash_by(ary);
uniq = rb_hash_values(hash);
}
else {
hash = ary_make_hash(ary);
uniq = rb_hash_values(hash);
}
RBASIC_SET_CLASS(uniq, rb_obj_class(ary));
ary_recycle_hash(hash);
return uniq;
}
您可以通过使用 uniq 的块版本来绕过它:
> [Foo.new(1,2), Foo.new(1,2), Foo.new(2,3)].uniq{|f| [f.a, f.b]}
=> [#<Foo:0x0000562e48937cc8 @a=1, @b=2>, #<Foo:0x0000562e48937c78 @a=2, @b=3>]
或Struct
改用:
F = Struct.new(:a, :b)
[F.new(1,2), F.new(1,2), F.new(2,3)].uniq
# => [#<struct F a=1, b=2>, #<struct F a=2, b=3>]
更新2:
实际上,就覆盖而言,如果您覆盖==
或eql?
. 当我覆盖eql?
它按预期工作时:
class Foo
attr_accessor :a, :b
def initialize(a, b)
@a = a
@b = b
end
def eql?(other)
(@a == other.a && @b == other.b)
end
def hash
[a, b].hash
end
def to_s
"#{@a}: #{@b}"
end
end
a = [
Foo.new(1, 1),
Foo.new(1, 2),
Foo.new(2, 1),
Foo.new(2, 2),
Foo.new(2, 2)
]
a.uniq
#=> [#<Foo:0x0000562e483bff70 @a=1, @b=1>,
#<Foo:0x0000562e483bff48 @a=1, @b=2>,
#<Foo:0x0000562e483bff20 @a=2, @b=1>,
#<Foo:0x0000562e483bfef8 @a=2, @b=2>]
推荐阅读
- reactjs - 将 NextJS 静态站点迁移到 squarespace/cms
- javascript - Alpine.js 无法将 x-data 绑定到外部 js 文件中的 functin
- mysql - 如何在选择查询中使用案例别名
- flutter - 导入的图片库出错
- python - Windows / Linux SQLAlchemy 连接器转换类型错误
- java - 从 JNI_OnLoad 返回的错误 JNI 版本,Android 11 崩溃
- python - 'pip install 和有什么区别
' 和 'python -m pip install '? - elasticsearch - 如何在 ElasticSearch 中的索引期间从现有字段创建新字段?
- javascript - 将数组中的先前文本移动到最新文本下方而不是被覆盖
- javascript - localStorage 检查器在看起来不错时不起作用?