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

通过在我的类型上实现 [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,但我对更通用的解决方案感兴趣。)

有没有好的/干净的方法来做到这一点?或者我只是使用减少和类似的功能来总结列表中的点而不能使用.sum该列表上的方法?

回答

你可以添加一个专门的infix:<+>候选人,然后你可以使用[+]metaop:

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它。这可以被认为是一个错误:将调查。

  • The `sum` method in CORE.setting will not have the `Point` multi dispatch candidate in lexical scope, so it's not taking effect. This is a rather unfortunate side-effect of `[+]` calling `.sum` as a special case.

回答

为了回答您的问题,我发明了我称之为“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从切换classrole

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

    这是multi method sum声明。

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

    这是self.List[0].sum: self.List[1]代码。


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

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

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


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

    multi method sum ($_: ;; $/ = .List) { $0.sum: $1 }

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

首先,事实上,大多数人没有意识到Captures 是 Raku 中一项重要的未充分利用的人体工程学创新。Capture不只是用于捕获参数。它不仅仅是Match. 它是一种通用的“嵌套数据结构”类型。

其次,沿着类似的思路,$/不仅仅是当前的Match变量。它也是Captures 的一个非常方便的自动解构,即通用嵌套数据结构的自动解构。

我一直在琢磨如何以下以最佳方式引入乐文化,但我认为这将是巨大的,有对应三个标点符号变量适当优雅/实用/细致入微的功能$_$/$!

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


以上是允许方法对我的类型列表进行操作?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>