在Perl 6中使用具有对象键的哈希

Eug*_*sky 4 perl6

我正在尝试Hash使用非字符串键,在我的情况下使用数组或列表.

> my %sum := :{(1, 3, 5) => 9, (2, 4, 6) => 12}
{(1 3 5) => 9, (2 4 6) => 12}
Run Code Online (Sandbox Code Playgroud)

现在,我不明白以下几点.

如何检索现有元素?

> %sum{(1, 3, 5)}
((Any) (Any) (Any))

> %sum{1, 3, 5}
((Any) (Any) (Any))
Run Code Online (Sandbox Code Playgroud)

如何添加新元素?

> %sum{2, 4} = 6
(6 (Any))
Run Code Online (Sandbox Code Playgroud)

Eli*_*sen 7

有几件事情是怎么回事:首先,如果使用(1,2,3)作为重点,Rakudo的Perl 6会认为这是3个键的切片:1,23.由于这些都不存在于对象哈希中,所以你得到了((Any) (Any) (Any)).

因此,您需要指明您希望将列表视为您想要值的单个键.你可以做到这一点$(),所以%sum{$(1,3,5)}.但是,这并没有给出预期的结果.其背后的原因如下:

> say (1,2,3).WHICH eq (1,2,3).WHICH
False
Run Code Online (Sandbox Code Playgroud)

对象哈希在内部将对象键入其.WHICH值.目前,Lists 被视为值类型,因此每个 List都有不同的值.WHICH.这使得它们不适合用作对象哈希中的键,或者在默认情况下使用它们的其他情况下(例如.unique,Sets,Bags和Mixes).

我实际上正在努力使上述eq回报True不久:这应该是2018.01编译器版本,其中还有一个Rakudo Star版本.

顺便说一句,每当你使用对象哈希值和整数值时,你可能会更好地使用Bags.由于上述原因,在这种情况下还没有唉.

你可以通过使用augment class List和添加一个.WHICH方法来实现这项工作,但我建议不要这样做,因为它会干扰任何未来的修复.


pio*_*ojo 5

Elizabeth的答案是可靠的,但是在创建该特性之前,我不明白为什么你不能创建一个Key用作散列键的类,它将具有一个显式散列函数,该函数基于其值而不是它的位置.记忆.这个哈希函数,用于列表中的放置和相等测试,是.WHICH.此函数必须返回一个ObjAt对象,该对象基本上只是一个字符串.

class Key does Positional {
  has Int @.list handles <elems AT-POS EXISTS-POS ASSIGN-POS BIND-POS push>;
  method new(*@list) { self.bless(:@list); }
  method WHICH() { ObjAt.new(@!list.join('|')); }
}

my %hsh{Key};
%hsh{Key.new(1, 3)} = 'result';
say %hsh{Key.new(1, 3)}; # output: result
Run Code Online (Sandbox Code Playgroud)

请注意,我只允许密钥包含Int.这是一种相当自信的简单方法,没有元素的字符串值包含'|' 字符,尽管有不同的元素,但可以使两个键看起来相同.然而,这不是对硬化调皮users-- 4 but role :: { method Str() { '|' } }Int该stringifies违法值.如果你使用.WHICH递归方式,你可以使代码更强大,但我会把它作为练习.

Key堂课也比你真正需要的还要小.拥有一个@.list成员并定义就足够了.WHICH.我定义了AT-POS和朋友,因此Key可以将其编入索引,推送到,并以其他方式对待Array.

  • 从https://github.com/rakudo/rakudo/commit/202459ce0b9cf0开始,有一个`ValueObjAt`对象,表明它是一个值为ObjAt的值类型.因此,您的`方法WHICH`应该更改为`ValueObjAt.new(@!list.join('|'))`以确保它在任何地方都被识别为值类型.请注意,`ValueObjAt`只是`ObjAt`的一个空子类,以便更容易地进行值类型检查. (2认同)