【问题标题】:How to use hyperoperators with Scalars that aren't really scalar?如何将超算子与非标量的标量一起使用?
【发布时间】:2020-12-16 18:29:21
【问题描述】:

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

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

我有一个包含新哈希键的数组:@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,把它当作一个标量。不,真的。”?

我尝试了一个显式的 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”,这可以解释为什么它实际上并没有按照我的意思做,但是否有可能让我使用的超级操作符版本更少更明确?我的大脑还没有完全适应 Raku 的工作方式,无法可靠地告诉 WIM。

【问题讨论】:

标签: raku rakudo


【解决方案1】:

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

一个惯用选项:

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

X metaoperator


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

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


我原以为超操作有两个测试控制递归:

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

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


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 值结合时,超操作明确拒绝在右侧或左侧使用 Associatives。


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

由于Associative 中键的随机顺序,这永远不会起作用。 这曾经死掉,但有一个关于Pair.new()的非常LTA错误 找不到合适的人选。


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

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"。)


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

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

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


添加一个自定义的 op 解包:

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 文字...)

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

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

有关{...} 序列何时为Hash 的解释,以及如何强制它为一个,请参阅my answer 到 Raku SO Hash 还是Block ?.


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

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

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

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

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

【讨论】:

  • @MarkReed 我已经用dd().raku.say for ... 和AFAIK 完成了所有测试,my %hash = @keys X=> $set;my %hash = ({ $_ => $set.clone } for @keys); 创建的%hash-es 是相同的。 @raiph 你是在暗示他们不是吗?或者它们(两者)都是不正确的规范? [我在 Rakudo 2021.06]。
猜你喜欢
  • 2020-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多