Raku是否支持运行时动态创建子例程签名接收具名参数
Raku中实现动态接收任意具名参数的方案
不需要手动动态构造方法签名,Raku原生语法直接支持该需求
最优实现:用捕获任意具名参数的语法
Raku 签名中*%opt的写法可以捕获所有调用时传入的、未提前声明的具名参数,参数名作为哈希键,参数值作为哈希值,完全不需要提前预知支持的选项名,直接改造原有方法即可:
method options(*%opt) { # 原有解析合法选项的逻辑无需改动 if !@!valid-options { my $out = (cmd 'bin/md2html -h').out; my @matches = $out ~~ m:g/\s'--'(<-[\s]>+)/; for @matches -> $match { push @!valid-options, $match[0].Str; } } # 处理传入的具名参数 for %opt.kv -> $opt-name, $opt-value { # 自动跳过显式传False的参数,比如:!ftables这种写法 next unless $opt-value; unless $opt-name (elem) @!valid-options { warn "'$opt-name' is not a valid option"; next; } push @!options, "--$opt-name"; } }
改造完成后,你期望的调用形式可以直接正常运行:
$obj.options: :ftables, :ftasklists, :github;
如果需要兼容原有的位置参数传法,只需要把签名改成method options(*@pos-opts, *%named-opts),在方法内补充对@pos-opts的处理逻辑即可,两种传参方式可以混合使用。
之前尝试的动态构造签名方案失败的原因
核心问题是没有把构造好的Signature对象实际绑定到函数上:
# 错误写法:这里的$sig只是一个普通位置形参的名字,和你之前定义的$sig变量没有任何关系 my &blah = -> $sig { say 'this works too' } ;
如果确实要手动给函数绑定签名,需要调用set-sig方法,示例如下:
my @params = Parameter.new(name => ':$option1', type => Bool, :!default); my $sig = Signature.new(params => @params); my &blah = -> { say 'this works too' }; &blah.set-sig($sig); &blah(:option1); # 此时可以正常调用
但该方案完全不适配你的场景,存在几个硬伤:
- 时序不匹配:类的方法签名在编译阶段就需要确定,而你的合法选项列表是第一次调用方法时才从外部命令输出解析得到的
- 维护成本高:每次合法选项列表更新,都需要重新构造签名、绑定到方法上
- 冗余:
*%opt是Raku专门为「不确定传入具名参数名」的场景设计的原生语法,稳定性和性能都远高于手动构造签名。
内容的提问来源于stack exchange,提问作者StevieD
相关产品推荐
相关产品推荐

