允许方法对我的类型列表进行操作?

问题描述

通过在我的类型上实现 [multi?|sub?] 方法,Raku 可以很容易地支持我的新类型上的现有功能。但是,我想知道它是否还提供了一种方法,可以将现有(或新)方法应用于我的类型的列表或其他位置集合(不增加列表,这是通往疯狂之路的第一步......)。>

更具体地说,这就是我正在谈论的内容,使用 Point 类,该类似乎是每个人处理此类事情的首选示例:

class Point { 
    has $.x; has $.y; 
    method sum(Point $p) { Point.new: :x($!x + $p.x) :y($!y + $p.y) }
}

my $p1 = Point.new: :8x :3y;

my $p2 = Point.new: :1x :9y;

$p1.sum: $p2; # this works     # OUTPUT: «Point.new(x => 9,y => 12)»

($p1,$p2).sum;  # This is what I want to be able to do

(我知道,在 .sum特定情况下,会有一个解决方案涉及在 Numeric 上实现 Point 强制转换方法,但是我对更通用的解决方案感兴趣。)

有没有好的/干净的方法来做到这一点?还是我只是使用归约和类似的特征来对 List 中的 Points 求和,而不能在该 List 上使用 .sum 方法?

解决方法

您可以添加一个专门的 infix:<+> 候选对象,然后您可以使用 [+] 元操作:

multi sub infix:<+>(Point:D \a,Point:D \b) { 
    a.sum(b)
}

say [+] $p1,$p2;  # Point.new(x => 9,y => 12)

List.sum 方法确实在内部使用 infix:<+>,但遗憾的是它首先尝试调用 Numeric。这可能被视为一个错误:将调查该问题。

,

为了回答您的问题,我发明了我称之为“4 步重新路由”的方法。这有四个步骤:

  1. 想要控制的模式匹配复合数据结构;

  2. 将匹配的数据结构强制转换为新类型;

  3. 模式匹配在新类型上调用的后续例程;

  4. 根据需要重新路由匹配的例程。


我尝试了多种方法来实现这 4 步重新路由,有些使用 OO,有些只是函数。以下是我为您的特定示例提出的解决方案的最短路径:

role Point {                                              # <-- Make `role`
    has $.x; has $.y; 
    multi method sum(Point $p) { Point.new: :x($!x + $p.x) :y($!y + $p.y) }

    multi method COERCE ($_) { $_ but Point }             # <-- Hook `sum`
    multi method sum { self.List[0].sum: self.List[1] }   # <-- Reroute `sum`
}

my $p1 = Point.new: :8x :3y;
my $p2 = Point.new: :1x :9y;

say Point($p1,$p2).sum; # OUTPUT: «Point.new(x => 9,y => 12)»

让我们回顾一下我开始的四个重新路由步骤以及我如何在上面实现它们:

  1. 想要控制的模式匹配复合数据结构;

    say Point($p1,$p2).sum; 中,我插入了一个 Point(...) 调用。因为 Point 是一种触发 Raku 强制协议的类型。这是开始模式匹配过程的一种方式。协议的一部分是在某些情况下调用类型的 COERCE 方法。所以我在你的类型中添加了一个 multi method COERCE... 方法,它被调用了。在这种情况下,这个调用的实际“模式匹配”方面是最小的——只是 $_——但我会在下面这个解决方案的另一个版本中详细说明。

  2. 将匹配的数据结构强制转换为新类型;

    这是 $_ but Point 代码。混入 Point 是最小但足够的强制。 (请注意,它可以反过来写 - Point but $_ 并且它仍然可以工作。)为了完成这项工作,我将 Point 的类型声明符从 class 切换到 {{ 1}}。

  3. 模式匹配在新类型上调用的后续例程;

    这是 role 声明。

  4. 根据需要重新路由匹配的例程。

    这是 multi method sum 代码。


作为第 1 步中更具体的模式匹配的说明:

self.List[0].sum: self.List[1]

如果您要使用这 4 步重新路由,那么上述签名模式可能非常明智。


为步骤 4 提供更简洁代码的习惯用法:

    multi method COERCE (List $_ (Point,Point)) { $_ but Point }

这看起来非常难看。为了什么?为什么我把它包括在内?好吧,我希望你能看到它暗示的潜力。这真的与你的问题或我的回答无关。但我不遵循关于何时分享某些东西的理性规则。我希望我能特别吸引你,@codesections,以及任何能读懂我心思的人。

首先,事实上,大多数人都没有意识到 multi method sum ($_: ;; $/ = .List) { $0.sum: $1 } 是 Raku 中一项未被充分利用的重要人体工程学创新。 Capture 不仅用于捕获参数。它不仅仅是 Capture 的父类。这是一种通用的“嵌套数据结构”类型。

第二,类似的,Match 不仅仅是当前的 $/ 变量。对于 Match 来说,这也是一种非常方便的自动解构,也就是说,对通用嵌套数据结构进行自动解构。

我一直在考虑如何最好地将以下内容引入 Raku 文化,但我认为拥有与三个标点变量 Capture$_$/

我已经将我的稻草人提案(在我的脑海中)命名为“这是数据,好吗?”。注意它是三个字。在过去的几年里,我已经在脑海中想出了很多细节,但我希望你能凭直觉开始想象,如果我们沿着我建议的道路前进,我们可以走向何方。我真的应该把它写成一个要点;这个 SO 答案的奇怪附录是首付款。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...