使用 `eqv` 进行对象哈希查找

Object Hash Lookup with `eqv`

有没有一种方法可以在使用对象键时使用 eqv 查找哈希值而不循环遍历键值对?

通过在声明时指定键的类型,可以在散列中使用对象键:

class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';

但是,键查找使用身份运算符 ===,只有当它是同一对象而不是等效对象时,它才会 return 值:

my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime;   # True
say $a === $a-prime;   # False
say %h{$a};            # A
say %h{$a-prime};      # (Any)

查看 documentation for "===",最后一行表明该运算符基于 .WHICH 并且 "... 所有值类型必须覆盖方法 WHICH。" 这就是为什么如果您创建两个具有相同字符串值的单独项目,"===" returns .

my $a = "Hello World";
my $b = join " ", "Hello", "World";

say $a === $b;     # True even though different items - because same value
say $a.WHICH ;     # "Str|Hello World"
say $b.WHICH ;     # (same as above) which is why "===" returns True

因此,与其创建自己的容器类型或使用 subscripts 的某些挂钩,不如复制 "value types" 的方式 - 即屠夫(某种程度上)的想法身份。上面显示的字符串的 .WHICH 方法只是 returns 类型名称和内容与 '|' 连接。为什么不做同样的事情;

class Foo {
    has $.bar;
    multi method WHICH(Foo:D:) { "Foo|" ~ $!bar.Str }
}

my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';

my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime;   # True
say $a === $a-prime;   # True
say %h{$a};            # A
say %h{$a-prime};      # A

当然,成本很小 - 这个 class 的实例的身份概念,好吧 - 可以说很有趣。有什么影响?唯一立即想到的是,如果您打算使用某种对象持久性框架,它会将 现在看起来相同 的不同实例压缩成一个(也许)。

具有相同属性数据的不同对象将无法区分 - 这就是为什么我在发布此答案之前犹豫不决的原因。 OTOH,它是你的 class,它是你的应用程序,因此,根据 size/importance 等,这可能是一个很好的方法。

如果您不喜欢内置后环固定,请提供您自己的。

class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';


multi sub postcircumfix:<{ }>(\SELF, WhateverCode $c) is raw {
    gather for SELF.keys -> $k {
        take $k if $c($k)
    }
}

dd %h;
dd %h{* eqv $a};

输出

Hash[Any,Foo] %h = (my Any %{Foo} = (Foo.new(bar => 1)) => "A", (Foo.new(bar => 2)) => "B")
(Foo.new(bar => 1),).Seq