如何将超级运算符与不是真正标量的标量一起使用?

How to use hyperoperators with Scalars that aren't really scalar?

我想对集合进行哈希处理。好吧,SetHashes,因为它们需要可变。

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

我有一个包含新散列键的数组:@keys

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

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

有效:

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

(优先级需要括号;没有它们,对 %hash 的赋值是 for 循环主体的一部分。我可以将其更改为非后缀 for 循环或进行其他几个小的更改中的任何一个,以稍微不同的方式获得相同的结果,但这不是我在这里感兴趣的。)

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

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

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

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

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

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

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 个元素的集合。这就是我想要的 - 映射到集合的键。但相反,它将它映射到集合的 元素 ,并且超级运算符为该大小组合指向了错误的方向。

那么为什么它在右边递归呢?我认为 Scalar 容器可以防止这种情况发生。文档说它可以防止扁平化;这个递归怎么不展平?区别是什么?

错误消息说我正在使用的超级操作者的版本是“non-dwimmy”,这可以解释为什么它实际上没有按照我的意思做,但可能有一个更不那么dwimmy的版本让我更明确一点?我的大脑还没有很好地适应 Raku 的工作方式,以便能够可靠地告诉 WIM。

I'm looking for a clean way to initialize the hash.

一个惯用选项:

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

参见 X metaoperator


The documentation says ... a Scalar container ... prevents flattening; how is this recursion not flattening? What's the distinction being drawn?

猫是动物,但动物不一定是猫。展平可能会递归执行,但某些递归执行的操作不会展平。如果看到 Scalar,递归展平就会停止。但超操作并没有变平。我明白你的意思,但这不是真正的问题,或者至少不是解决方案。


我还以为超运算有两个控制递归的测试:

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

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


A SetHash 不执行 Iterable 角色。所以我认为拒绝对其进行过度操作是另外一回事。


我刚刚搜索了源代码,在当前的 Rakudo 源代码中找到了两个匹配项,都在 Hyper 模块中,this one 是我们正在处理的特定匹配项:

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

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


追寻“责备”(跟踪谁做了什么改变)我到达了 the commit "Die on Associative <<op>> Iterable" 上面写着:

This can never work due to the random order of keys in the Associative. This used to die before, but with a very LTA error about a Pair.new() not finding a suitable candidate.


也许可以细化此行为,使决定因素首先是操作数是否扮演 Iterable 角色,然后如果扮演 Associative 角色,它就会死亡,但是如果不是,它是否被接受为单个项目?

A search for "can never work reliably" in GH/rakudo/rakudo issues 产生零个匹配项。

也许提交一个问题? (更新我提交"RFC: Allow use of hyperoperators with an Associative that does not do Iterable role instead of dying with "can never work reliably"。)


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

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

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


添加自定义 op unwraps en passant:

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);

(The parens seem to be needed so it can tell that the curlies are a block instead of a Hash literal...)

没有。 curlies 中的特定代码是 Block,无论它是否在括号中。

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

有关 {...} 序列何时为 Hash 以及如何将其强制为一个的解释,请参阅 my answer 到 Raku SO Is HashBlock?.


没有括号你写了这个:

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

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

({ block of code } for @keys) 部分加上括号完全改变了代码的含义。

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