在 Raku 中使用带有对象键的哈希

问题描述 投票:0回答:2

我正在尝试使用非字符串键制作

Hash,在我的例子中是数组或列表。

> my %sum := :{(1, 3, 5) => 9, (2, 4, 6) => 12}
{(1 3 5) => 9, (2 4 6) => 12}

现在,我不明白以下内容。

如何检索现有元素?

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

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

如何添加新元素?

> %sum{2, 4} = 6
(6 (Any))
raku
2个回答
7
投票

这里发生了几件事情:首先,如果您使用

(1,2,3)
作为键,Rakudo Perl 6 会认为这是 3 个键的切片:
1
2
3
。 由于这些都不存在于对象哈希中,因此您会得到
((Any) (Any) (Any))

因此,您需要表明您希望列表被视为您想要其值的单个键。 您可以使用

$()
来完成此操作,因此
%sum{$(1,3,5)}
。 然而,这并没有给你预期的结果。 其背后的原因如下:

> say (1,2,3).WHICH eq (1,2,3).WHICH
False

对象哈希在内部将对象键为其

.WHICH
值。 目前,
List
被视为值类型,因此每个
List
有不同的
.WHICH
。 这使得它们不适合用作对象哈希中的键,或者在默认使用它们的其他情况下(例如
.unique
Set
s、
Bag
s 和
Mix
es)。

我实际上正在努力使上面的

eq
返回
True
不久:这应该会进入2018.01编译器版本,Rakudo Star版本也将基于该版本。

顺便说一句,任何时候你使用对象哈希和整数值时,使用

Bag
可能会更好。 可惜由于上述原因,目前还没有出现这种情况。

实际上,您可以通过使用

augment class List
并在其上添加
.WHICH
方法来完成这项工作,但我建议不要这样做,因为它会干扰任何未来的修复。


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

请注意,我只允许密钥包含

Int
。这是一种相当确信没有元素的字符串值包含“|”的简单方法字符,这可以使两个键看起来相同,尽管具有不同的元素。然而,这并没有针对顽皮的用户进行强化——
4 but role :: { method Str() { '|' } }
是一个字符串化为非法值的
Int
。如果您递归地使用
.WHICH
,您可以使代码变得更强大,但我会将其作为练习。

这个

Key
课程也比你真正需要的要复杂一些。拥有一个
@.list
成员并定义
.WHICH
就足够了。我定义了 AT-POS 和朋友,因此
Key
可以被索引、推送到或以其他方式视为
Array

© www.soinside.com 2019 - 2024. All rights reserved.