针对您的问题,我发明了我称之为“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)»
让我们回顾一下我开始的四个重新路由步骤以及我在上面是如何实现它们的:
-
想要控制的模式匹配复合数据结构;
在say Point($p1, $p2).sum; 中,我插入了一个Point(...) 调用。因为Point 是一种触发Raku 强制协议的类型。这是开始模式匹配过程的一种方式。协议的一部分是在某些场景中调用类型的COERCE 方法。所以我在你的类型中添加了一个multi method COERCE... 方法,它被调用了。在这种情况下,这个调用的实际“模式匹配”方面是最小的——只是$_——但我将在下面这个解决方案的另一个版本中详细说明。
-
将匹配的数据结构强制转换为新类型;
这是$_ but Point 代码。混入Point 是最小但足够的强制。 (请注意,它可以反过来写——Point but $_,它仍然可以工作。)为了完成这项工作,我将Point 的类型声明器从class 切换到role。
-
模式匹配新类型上调用的后续例程;
这是multi method sum 声明。
-
根据需要重新路由匹配的例程。
这是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 来说,它也是一个非常方便的自动解构,也就是通用嵌套数据结构的自动解构。
我一直在考虑如何最好地将以下内容引入 Raku 文化,但我认为拥有与三个标点符号变量$_、@ 对应的适当优雅/实用/细微差别的功能会很棒987654345@和$!。
我已将我的稻草人提案(在我的脑海中)命名为“它是数据,好吗?”。注意它是三个字。在过去的几年里,我已经在脑海中想出了很多关于这个的细节,但我希望你能直观地开始想象如果我们沿着我建议的道路前进,我们可以去哪里。我真的应该把它写下来;这个 SO 答案的奇怪附录是首付。