将子集中正则表达式的命名捕获放入签名中的变量中
Put named capture from regex in Subset into a variable in the signature
考虑
subset MySubset of Str where * ~~ /^ \d $<interesting> = ( \d+ ) $/;
现在我想在我的签名中使用子集作为类型,但是通过解包将捕获的部分放入变量中,有点像
sub f( MySubset $( :$interesting ) )
{
say $interesting;
}
f( "12345678" ); # should say 2345678
这当然行不通。甚至可以这样做吗?
我很确定 where
s(和 subset
s)只需回答 True
/False
。布拉德同意。
基本上总是元编程问题的答案,但我认为你不是这个意思(而且几乎从来没有深入挖掘)。
因此,这里有几种方法可以让您获得接近您想要的东西。
基于 Brad 的见解的(由于 MONKEY
ing 而可疑)解决方案:
use MONKEY;
augment class Str {
method MyMatch { self ~~ / ^ \d $<interesting> = ( \d+ ) $ / }
}
class MyMatch is Match {}
sub f( MyMatch() $foo (:$interesting) ) { say ~$interesting }
f( "12345678" ); # 2345678
坏消息是,即使字符串不匹配,sub
调度仍然有效。该文档清楚地表明强制方法(上面的method MyMatch
)目前不能发出失败信号:
The method is assumed to return the correct type — no additional checks on the result are currently performed.
人们可以希望有一天 augment
ing a class 将是一件正式值得尊敬的事情(而不是要求 use MONKEY...
)并且强制可以表示失败。那时我认为这可能是一个不错的解决方案。
上面的变体绑定到 $/
所以你可以使用 $<interesting>
:
use MONKEY;
augment class Str {
method MyMatch { self ~~ / ^ \d $<interesting> = ( \d+ ) $ / }
}
class MyMatch is Match {}
sub f( MyMatch() $/ ) { say ~$<interesting> }
f( "12345678" ); # 2345678
另一种避免 MONKEY
绕过的方法是按照您的建议使用 subset
,但将正则表达式和子集分开:
my regex Regex { ^ \d $<interesting> = ( \d+ ) $ }
subset Subset of Str where &Regex;
sub f( Subset $foo ; $interesting = ~($foo ~~ &Regex)<interesting> )
{
say $interesting;
}
f( "12345678" ); # 2345678
备注:
正则表达式至少解析输入值两次。首先在Subset
中决定调用是否派发到sub
。但是匹配的结果被丢弃了——值以字符串形式到达。然后正则表达式再次匹配 ,因此可以解构匹配。对于当前的 Rakudo,如果 sub
是 multi
,情况会更糟——正则表达式将被使用 three 次,因为 Rakudo 目前同时进行了一次试验绑定作为决定匹配哪个 multi
的一部分,然后 另一个 绑定实际调用。
参数可以根据之前的参数进行设置。我已经用 $interesting
做到了。签名可以包含属于调度决策一部分的参数,而其他参数则不是。它们由分号分隔。我将这两个特征结合起来创建了另一个变量,我想你可能会认为这是一件积极的事情。您的评论表明您不这样做,这非常合理。 :)
子签名解包是关于将值转换为 Capture
并与之匹配。
class Point {
has ( $.x, $.y );
}
my ( :$x, :$y ) := Point.new( x => 3, y => 4 ).Capture;
say "[$x,$y]"; # [3,4]
因为 Str
没有名为 $.interesting
的 public 属性,它不会匹配。
子集只是额外的代码,用于比其他方式更完整地检查值。它不会将值转换为新类型。
如果您使用 $<interesting>
.
可能会更有效
sub f( MySubset )
{
say $<interesting>;
}
当然,因为块有自己的$/
,这也行不通。
虽然将信息从子集传递到签名可能很好,但我不知道如何去做。
附带说明一下,where
已经进行了智能匹配,因此在其中使用 ~~
是一个非常糟糕的主意。
这基本上就是您的子集的工作方式:
"12345678" ~~ ( * ~~ /…/ )
在这种特殊情况下,您可以只使用 .substr
sub f( MySubset $_ ) {
.substr(1)
}
我想不出使用 subset
类型的方法,但是 是 一种方法 - 有点...创造力 - 进行匹配并在签名中解压。
Match
继承自 Capture
,因此在签名中解压一个很简单——只要我们能安排一个包含我们希望的 Match
的参数打开包装。一种方法是引入一个带有默认值的进一步参数。我们不能真正阻止任何人传递给它——尽管我们可以通过使用匿名命名参数来让它变得很痛苦。因此,如果我们这样写:
sub foo($value, :$ (:$col, :$row) = $value.match(/^$<col>=[<:L>+]$<row>=[\d+]$/)) {
say $col;
say $row;
}
并将其命名为foo("AB23")
,输出为:
「AB」
「23」
最后,我们可以将规则分解为命名标记,从而实现:
my token colrow { ^$<col>=[<:L>+]$<row>=[\d+]$ }
sub foo($value, :$ (:$col, :$row) = $value.match(&colrow)) {
say $col;
say $row;
}
考虑
subset MySubset of Str where * ~~ /^ \d $<interesting> = ( \d+ ) $/;
现在我想在我的签名中使用子集作为类型,但是通过解包将捕获的部分放入变量中,有点像
sub f( MySubset $( :$interesting ) )
{
say $interesting;
}
f( "12345678" ); # should say 2345678
这当然行不通。甚至可以这样做吗?
我很确定 where
s(和 subset
s)只需回答 True
/False
。布拉德同意。
基本上总是元编程问题的答案,但我认为你不是这个意思(而且几乎从来没有深入挖掘)。
因此,这里有几种方法可以让您获得接近您想要的东西。
基于 Brad 的见解的(由于 MONKEY
ing 而可疑)解决方案:
use MONKEY;
augment class Str {
method MyMatch { self ~~ / ^ \d $<interesting> = ( \d+ ) $ / }
}
class MyMatch is Match {}
sub f( MyMatch() $foo (:$interesting) ) { say ~$interesting }
f( "12345678" ); # 2345678
坏消息是,即使字符串不匹配,sub
调度仍然有效。该文档清楚地表明强制方法(上面的method MyMatch
)目前不能发出失败信号:
The method is assumed to return the correct type — no additional checks on the result are currently performed.
人们可以希望有一天 augment
ing a class 将是一件正式值得尊敬的事情(而不是要求 use MONKEY...
)并且强制可以表示失败。那时我认为这可能是一个不错的解决方案。
上面的变体绑定到 $/
所以你可以使用 $<interesting>
:
use MONKEY;
augment class Str {
method MyMatch { self ~~ / ^ \d $<interesting> = ( \d+ ) $ / }
}
class MyMatch is Match {}
sub f( MyMatch() $/ ) { say ~$<interesting> }
f( "12345678" ); # 2345678
另一种避免 MONKEY
绕过的方法是按照您的建议使用 subset
,但将正则表达式和子集分开:
my regex Regex { ^ \d $<interesting> = ( \d+ ) $ }
subset Subset of Str where &Regex;
sub f( Subset $foo ; $interesting = ~($foo ~~ &Regex)<interesting> )
{
say $interesting;
}
f( "12345678" ); # 2345678
备注:
正则表达式至少解析输入值两次。首先在
Subset
中决定调用是否派发到sub
。但是匹配的结果被丢弃了——值以字符串形式到达。然后正则表达式再次匹配 ,因此可以解构匹配。对于当前的 Rakudo,如果sub
是multi
,情况会更糟——正则表达式将被使用 three 次,因为 Rakudo 目前同时进行了一次试验绑定作为决定匹配哪个multi
的一部分,然后 另一个 绑定实际调用。参数可以根据之前的参数进行设置。我已经用
$interesting
做到了。签名可以包含属于调度决策一部分的参数,而其他参数则不是。它们由分号分隔。我将这两个特征结合起来创建了另一个变量,我想你可能会认为这是一件积极的事情。您的评论表明您不这样做,这非常合理。 :)
子签名解包是关于将值转换为 Capture
并与之匹配。
class Point {
has ( $.x, $.y );
}
my ( :$x, :$y ) := Point.new( x => 3, y => 4 ).Capture;
say "[$x,$y]"; # [3,4]
因为 Str
没有名为 $.interesting
的 public 属性,它不会匹配。
子集只是额外的代码,用于比其他方式更完整地检查值。它不会将值转换为新类型。
如果您使用 $<interesting>
.
sub f( MySubset )
{
say $<interesting>;
}
当然,因为块有自己的$/
,这也行不通。
虽然将信息从子集传递到签名可能很好,但我不知道如何去做。
附带说明一下,where
已经进行了智能匹配,因此在其中使用 ~~
是一个非常糟糕的主意。
这基本上就是您的子集的工作方式:
"12345678" ~~ ( * ~~ /…/ )
在这种特殊情况下,您可以只使用 .substr
sub f( MySubset $_ ) {
.substr(1)
}
我想不出使用 subset
类型的方法,但是 是 一种方法 - 有点...创造力 - 进行匹配并在签名中解压。
Match
继承自 Capture
,因此在签名中解压一个很简单——只要我们能安排一个包含我们希望的 Match
的参数打开包装。一种方法是引入一个带有默认值的进一步参数。我们不能真正阻止任何人传递给它——尽管我们可以通过使用匿名命名参数来让它变得很痛苦。因此,如果我们这样写:
sub foo($value, :$ (:$col, :$row) = $value.match(/^$<col>=[<:L>+]$<row>=[\d+]$/)) {
say $col;
say $row;
}
并将其命名为foo("AB23")
,输出为:
「AB」
「23」
最后,我们可以将规则分解为命名标记,从而实现:
my token colrow { ^$<col>=[<:L>+]$<row>=[\d+]$ }
sub foo($value, :$ (:$col, :$row) = $value.match(&colrow)) {
say $col;
say $row;
}