如何使用 `|c` 捕获默认参数?

How do I capture default arguments with `|c`?

我这里有这个功能:

my @modifiers = <command option>;
sub triple(|c(Str:D $app1!, Str:D $app2!, Str:D $key! where .chars == 1, Str:D $mod where ($mod ~~ any @modifiers) = 'command' )) {
    print_template(|c);
}

sub print_template(*@values) {
   ...work done here...
}

我遇到的问题是,如果我在没有第 4 个参数的情况下调用它,例如 triple 'App1', 'App2', 'p';,默认的 $mod 参数不会传递给 print_template参数。

有办法做到这一点吗?

对于完整的上下文,这是这里的玩具程序:https://paste.debian.net/1226675/

好的,根据 IRC 中的回复,这似乎不可能。一种建议的解决方法:

sub triple(|c(Str:D $app1!,
              Str:D $app2!,
              Str:D $key! where .chars == 1,
              Str:D $mod where ($mod ~~ any @modifiers) = 'command' )) {
    my \d = \(|c[0..2], c[3] // 'command');
    print_template(|d);
}

TL;DR 1.问题的解释。 2. DRY 解决方案。 3. DRYest 解决方案:parameters-capture 函数。

问题的解释

呼叫分两步进行:

  1. 构造一个呼叫,不知道哪个routine会听到这个呼叫。这包括构造调用参数的捕获。在第 2 步之前,此捕获已经完成并已除尘,不可变。

  2. 看看有哪些常规候选人可能会绑定电话。尝试将捕获绑定到他们的签名。例程签名中的某些参数可能会指定默认值,以防缺少参数。

因此,给定一个捕获 c,您不能更改它来弥补其中不存在的任何参数,并且 Raku 不会自动创建一个新的捕获来伪装任何参数不在里面现在在里面。相反,您将不得不手动添加缺失值。

一个DRY解决方案

字符串 'command' 在您回答的建议解决方案中出现了两次。

编写 DRY 解决方案的一种方法是使用整个捕获,其中将包括所有传递的参数,然后 append 任何 parameters,其中相应的 参数 通过。也就是说,而不是:

my \d = \(|c[0..2], c[3] // 'command');

这样写:

my \d = \(|c, |($mod if not c[3]));

DRYest 解决方案:parameters-capture 函数

最终,您的场景需要的是一个完全忽略用于调用例程的 参数 的函数,而只是创建一个新的捕获,其中包含例程的所有 参数。然后一个人可以写,说:

print_template(|parameters-capture);

我觉得这很重要。这意味着遍历例程的参数数据。这大概会通过类似 &?ROUTINE.signature.params 的方式进行。但是 &?ROUTINE 是一个编译时变量,并且是相对于当前例程的,所以你如何在你从 例程中 调用的函数中得到它,你的参数是有兴趣吗?即使你能得到它,你如何从编译时参数数据结构到最终绑定到参数的 运行 时间值?这完全超过了我的工资等级。进行这种胆量编码可能比在 Perl 世界中要容易得多,在 Perl 世界中它意味着 C 编码,但仍然如此。

获得相同总体结果的另一种方法(可能是我要采用的方法)是将其拆分为 multi 并根据参数数量进行分派。这是一种可能的方式(将共享参数的验证移至 proto:

my @modifiers = <command option>;
proto triple(Str:D, Str:D, Str:D $ where .chars == 1, $?) {*}
multi triple(|c($, $, $, Str:D $ where any @modifiers))   { print_template |c }
multi triple(|c($, $, $))                                 { print_template |c, 'command' }

sub print_template(*@values) {
   # work done here
}

say triple 'App1', 'App2', 'p';