【问题标题】:In Perl 6, can I use an Array as a Hash key?在 Perl 6 中,我可以使用 Array 作为 Hash 键吗?
【发布时间】:2018-10-07 15:05:17
【问题描述】:

在 Hash 文档中,Object keys 部分似乎暗示您可以使用任何类型作为 Hash 键,只要您指出,但我在尝试使用数组作为键时遇到问题:

> my %h{Array};
{}
> %h{[1,2]} = [3,4];
Type check failed in binding to parameter 'key'; expected Array but got Int (1)
in block <unit> at <unknown file> line 1

可以这样做吗?

【问题讨论】:

  • 如果您将%h{[1,2]} = [3,4]; 更改为%h{item [1,2]} = [3,4];,它会阻止数组扩展为切片并将其保留为数组,以便用作键。
  • @callyalater: 是的,但它没有多大用处:-(。见我的回答。
  • @raiph 对于我的用例,我可以保证输入将是整数,因此使用Set([1,2])~[1, 2] 可以完美地满足我的需求。 .perl 看起来很有趣,但我不确定在正常开发过程中实际使用它的频率。
  • 谢谢。我删除了我之前建议~ 的评论和答案,但你大概知道我的答案从~ 前缀演变为.perl 后缀。这是因为,afaik,除了简洁之外,它至少与其他两种 hack 一样好,并且在许多情况下比其他两种 hack 更好,即根据 Liz 的建议,常规字符串化 (~) 或 .Set.perl 区分 [1,2][2,1].Set 不区分 &lt;&lt;'a a' 'b'&gt;&gt;&lt;&lt;'a' 'a b'&gt;&gt;,常规字符串化(使用 ~)不区分。 Anyhoo,正如您所说,如果您的所有数组/列表元素都是整数,~ 将可以正常工作。
  • @raiph 我也认为向后来的答案寻求者表明答案是很重要,您不能直接使用数组作为哈希键,因为伊丽莎白的回答中对此进行了解释。

标签: arrays hash hashmap key raku


【解决方案1】:

%h{[1,2]} = [3,4] 中的[1,2] 被解释为一个切片。所以它尝试分配%h{1}%{2}。而且由于密钥必须是Array,因此不能很好地进行类型检查。这就是错误消息告诉您的内容。

如果您逐项列出数组,它“确实”起作用:

my %h{Array};
%h{ $[1,2] } = [3,4];
say %h.perl;  # (my Any %{Array} = ([1, 2]) => $[3, 4])

但是,这可能不会得到你想要的,因为:

say %h{ $[1,2] };  # (Any)

这是因为对象哈希使用.WHICH 方法的值作为底层数组中的键。

say [1,2].WHICH; say [1,2].WHICH;
# Array|140324137953800
# Array|140324137962312

请注意,那些看似相同的数组的.WHICH 值是不同的。 那是因为Arrays 是可变的。就像Lists 一样,所以这真的行不通。

那么你想达到什么目的?如果数组中值的顺序不重要,您可以使用Sets 作为键:

say [1,2].Set.WHICH; say [1,2].Set.WHICH
# Set|AEA2F4CA275C3FE01D5709F416F895F283302FA2
# Set|AEA2F4CA275C3FE01D5709F416F895F283302FA2

请注意,这两个.WHICHes 是相同的。所以你可以这样写:

my %h{Set};
dd %h{ (1,2).Set } = (3,4); # $(3, 4)
dd %h; # (my Any %{Set} = ((2,1).Set) => $(3, 4))

希望这可以澄清事情。更多信息请访问:https://docs.raku.org/routine/WHICH

【讨论】:

  • 很好的答案,但人们可能会读到您写的内容,说不能使用数组。但这只是文字问题——my %h{Array}; my @array = 5,6; %h{ $@array } = 'foo'; say %h{ $@array }; # foo 工作正常,你甚至可以修改数组,它仍然可以工作:@array = 7,8; @array.append: 9,42; say %h{ $@array }; # foo。另外:%h { $@array } = $@array 可能会保持数组作为键并将数组的值作为关联值(未经测试,我需要睡觉)。
  • @raiph 虽然这似乎确实适用于插入,但您还需要将数组传递给 retrieve 值,这使得使用起来非常尴尬。我将尝试使用 Set 解决方案并报告。
  • @raiph:正如 Hunter McMillen 发现的那样,即使您不使用文字数组,也必须使用 same 数组来获取值。我认为这在现实生活中不会经常发生。因此我对Set 的建议只有在数组中值的顺序无关紧要时才有意义。
【解决方案2】:

如果您出于某种原因真的只对使用 Object Hash 感兴趣,请参阅 Liz 在此处的答案,尤其是a similar earlier question 的答案和 cmets。

this 答案的 (final1) 重点是使用 Array(如 [1,'abc',[3/4,Mu,["more",5e6],9.9],"It's {&lt;sunny rainy&gt;.pick} today"])作为常规字符串哈希键的简单方法。

基本原则是使用.perl 来逼近一个不可变的"value type" 数组,直到出现一个规范的不可变Positional 类型和更健壮的值类型.WHICH

使用数组作为哈希键的简单方法

my %hash;
%hash{ [1,2,3].perl } = 'foo';
say %hash{ [1,2,3].perl }; # displays 'foo'

.perl 将其参数转换为 Perl 6 代码的 string,这是该参数的 literal 版本。

say [1,2,3].perl;    # displays '[1, 2, 3]'

请注意如何添加空格,但这并不重要。

这不是一个完美的解决方案。如果您在键访问之间改变数组,您显然会得到错误的结果。不太明显,您会得到与.perl 中的任何限制或错误相对应的错误结果:

say [my %foo{Array},42].perl; # displays '[(my Any %{Array}), 42]'

1 希望这是我对您问题的最终回答的结尾。请参阅 my earlier 10th (!!) version of this answer 以讨论使用前缀 ~ 的替代方案以实现更有限但类似的效果和/或尝试在下面的 cmets 中了解我与 Liz 的交流。

【讨论】:

  • 我认为这里的问题确实是:(1,2,3) 目前不是一个值类型。我认为应该是文字列表。并且($a,$b,$c) 将取消变量以创建值类型。然后普通的对象散列会做正确的事情。
  • Joiner 常量与IterationEnd 具有相同的漏洞。它是可见的,因此可以被滥用。我更多地考虑以某种方式使用词汇Mu.new 类型的方法,其中对象的地址是“秘密”。不知道结果如何:-(
  • 谢谢。我喜欢列表文字的构造时间deep(递归)去conting,确保文字列表始终是冻结/不可变的。也可以是 List.new 上的副词 (:ro?)。如果列表随后被标记以便自省和编译器可以知道它是不可变的,那也很好。这应该会显着提高编写绝对不可变代码的简单性和一大类编译器优化。
猜你喜欢
  • 2017-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-16
  • 2014-06-16
  • 2010-09-17
  • 2010-11-30
  • 2013-08-07
相关资源
最近更新 更多