在 Perl 6 中使用带对象键的散列
Using a hash with object keys in Perl 6
我正在尝试制作一个 Hash
with non-string keys,在我的例子中是数组或列表。
> 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))
这里发生了几件事:首先,如果您使用 (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
不 被认为是值类型,因此 each List
有不同的 .WHICH
.这使得它们不适合用作对象哈希中的键,或者在默认情况下使用它们的其他情况下(例如 .unique
和 Set
s,Bag
s 和 Mix
es).
我实际上正在努力使它成为上面的 eq
return True
不久:这应该会进入 2018.01 编译器版本,其中还有 Rakudo Star 版本将根据
顺便说一句,任何时候你使用对象散列和整数值,你可能会更好地使用 Bag
s。唉,由于上述原因,在这种情况下还没有。
您实际上可以通过使用 augment class List
并在其上添加 .WHICH
方法来完成这项工作,但我建议不要这样做,因为它会干扰任何未来的修复。
Elizabeth 的回答是可靠的,但在创建该功能之前,我不明白为什么你不能创建一个 Key
class 用作哈希键,它将有一个显式哈希函数,它基于它的值而不是它在内存中的位置。这个散列函数,用于列表中的放置和相等性测试,是 .WHICH
。此函数必须 return 一个 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
class 也比您真正需要的要漂亮一些。有一个 @.list
成员并定义 .WHICH
就足够了。我定义了 AT-POS and friends,因此 Key
可以被索引、推送到,或者被视为 Array
。
我正在尝试制作一个 Hash
with non-string keys,在我的例子中是数组或列表。
> 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))
这里发生了几件事:首先,如果您使用 (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
不 被认为是值类型,因此 each List
有不同的 .WHICH
.这使得它们不适合用作对象哈希中的键,或者在默认情况下使用它们的其他情况下(例如 .unique
和 Set
s,Bag
s 和 Mix
es).
我实际上正在努力使它成为上面的 eq
return True
不久:这应该会进入 2018.01 编译器版本,其中还有 Rakudo Star 版本将根据
顺便说一句,任何时候你使用对象散列和整数值,你可能会更好地使用 Bag
s。唉,由于上述原因,在这种情况下还没有。
您实际上可以通过使用 augment class List
并在其上添加 .WHICH
方法来完成这项工作,但我建议不要这样做,因为它会干扰任何未来的修复。
Elizabeth 的回答是可靠的,但在创建该功能之前,我不明白为什么你不能创建一个 Key
class 用作哈希键,它将有一个显式哈希函数,它基于它的值而不是它在内存中的位置。这个散列函数,用于列表中的放置和相等性测试,是 .WHICH
。此函数必须 return 一个 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
class 也比您真正需要的要漂亮一些。有一个 @.list
成员并定义 .WHICH
就足够了。我定义了 AT-POS and friends,因此 Key
可以被索引、推送到,或者被视为 Array
。