9

我想做一个集合的哈希。好吧,SetHashes,因为它们需要是可变的。

事实上,我想用同一个 SetHash 的多个相同副本来初始化我的 Hash。

我有一个包含新哈希键的数组:@keys

而且我的 SetHash 已经在一个标量变量中初始化:$set

我正在寻找一种干净的方法来初始化哈希。

这有效:

my %hash = ({ $_ => $set.clone } for @keys);

(括号是优先级所必需的;没有它们,分配到%hashfor循环体的一部分。我可以将其更改为非后缀 for 循环或进行其他几个小的更改以获得相同的结果不同的方式,但这不是我在这里感兴趣的。)

相反,我有点希望我可以使用 Raku 的一个漂亮的超级操作员,可能是这样的:

my %hash = @keys »=>» $set;

$set是一个简单的字符串或数字,但一个 SetHash?

Array >>=>>> SetHash can never work reliably: order of keys in SetHash is indeterminate

很高兴知道,但我不希望它以任何顺序超过 RHS。这就是为什么我使用了超操作的右指向版本:所以它会根据需要复制 RHS 以将其与 LHS 匹配。在这种表达方式中,有没有办法说“哟,Raku,把它当作一个标量。不,真的。”?

我尝试了一个显式的 Scalar 包装器(这会使值更难获得,但这是一个实验):

my %map = @keys »=>» $($set,)

这给了我这样的信息:

Lists on either side of non-dwimmy hyperop of infix:«=>» are not of the same length while recursing
left: 1 elements, right: 4 elements

因此,它显然已经递归到左侧的列表中并找到了一个键,并试图将其映射到右侧具有 4 个元素的集合。这就是我想要的 - 映射到集合的键。但相反,它将它映射到集合的元素,并且超级操作员为这种大小组合指出了错误的方式。

那么为什么它会在右边递归呢?我认为标量容器会阻止这种情况。文档说它可以防止扁平化;这个递归怎么没有变平?有什么区别?

错误消息说我正在使用的超级操作器的版本是“非 dwimmy”,这可以解释为什么它实际上并没有按照我的意思做,但是可能有一个甚至更少的 dwimmy 版本让我变得更加明确的?我的大脑仍然没有很好地与 Raku 的工作方式保持一致,以便能够可靠地告诉 WIM。

4

1 回答 1

7

我正在寻找一种干净的方法来初始化哈希。

一种惯用的选择:

my %hash = @keys X=> $set;

请参阅X元运算符


文档说......一个Scalar容器......防止扁平化;这个递归怎么没有变平?有什么区别?

猫是动物,但动物不一定是猫。展平可能会递归执行,但某些递归执行的操作不会展平。如果看到 a ,则递归展平停止Scalar。但超操作并没有变平。我知道你来自哪里,但这不是真正的问题,或者至少不是解决方案。


我曾认为超操作有两个控制递归的测试:

  • 它是超nodal操作操作(例如.elems)吗?如果是这样,只需像并行浅层一样应用它map(所以不要递归)。(当前的文档非常强烈地暗示nodal只能有效地应用于方法,并且只能应用于List一个(或其增强)而不是任何可能过度操作的例程。这比我预期的要严格得多,我对此表示怀疑其真实性。)

  • 否则,是一个值Iterable吗?如果是这样,则递归到该值。通常, a 的值Scalar自动表现为它包含的值,这适用于这里。所以Scalars 无济于事。


ASetHash不扮演这个Iterable角色。所以我认为拒绝过度使用它是另一回事。


我刚刚搜索了源,在当前的 Rakudo 源中产生了两个匹配项,都在 Hyper 模块中,这个是我们正在处理的特定的一个:

    multi method infix(List:D \left, Associative:D \right) {
        die "{left.^name} $.name {right.^name} can never work reliably..."
    }

出于某种原因,当与另一侧作为值耦合时,超操作明确拒绝Associative在右侧或左侧使用 s 。List


在追求“责备”(跟踪谁做了什么改变)之后,我到达了提交“Die on Associative <<op>>Iterable”,它说:

由于Associative. 这以前死了,但有一个关于Pair.new() 没有找到合适的候选人的非常 LTA 错误。


也许可以改进这种行为,以便决定因素首先是操作数是否Iterable扮演角色,然后如果是,并且是Associative,它会死掉,但如果不是,它被接受为单个项目?

GH/rakudo/rakudo 问题中搜索“永远无法可靠地工作”会产生零匹配。

也许提出问题?(更新我提交了“RFC:允许使用具有不做可迭代角色的关联的超级操作符,而不是死于“永远无法可靠地工作”。)


现在我们需要找到一些其他的技术来阻止Iterable Associative不被拒绝。在这里,我使用Capture文字:

my %hash = @keys »=>» \($set);

这产生:{a => \(SetHash.new("b","a","c")), b => \(SetHash.new("b","a","c")), ....


添加一个自定义操作打开过路:

sub infix:« my=> » ($lhs, $rhs) { $lhs => $rhs[0] }
my %hash = @keys »my=>» \($set);

这产生了预期的结果:{a => SetHash(a b c), b => SetHash(a b c), ....


my %hash = ({ $_ => $set.clone } for @keys);

(似乎需要括号,所以它可以告诉卷曲是一个块而不是Hash文字......)

Block不,无论是否在括号中,花括号中的特定代码都是一个。

更一般地,术语位置形式的 Raku 代码{...}几乎总是一个Block.

有关何时{...}序列是 aHash以及如何强制它成为一个序列的解释,请参阅对 Raku SO Is that a Hashor a 的回答Block.


如果没有括号,您会这样写:

my %hash = { block of code } for @keys

它尝试迭代@keys,为每次迭代运行代码my %hash = { block of code }。代码失败,因为您无法将代码块分配给哈希。

将括号放在({ block of code } for @keys)零件周围会完全改变代码的含义。

现在它为每次迭代运行代码块。并且它将每次运行的结果连接成一个结果列表,每个结果都是Pair由代码生成的$_ => $set.clone。然后,当for迭代完成时,该结果对列表一次分配给my %hash

于 2020-12-17T03:30:47.953 回答