Raku 正则表达式:如何知道哪个组在交替时被捕获

Raku regex: How to know which group was captured at an alternation

使用 perl(以及几乎任何正则表达式风格),每个组都按顺序编号。

例如,这段代码:

'bar' =~ m/(foo)|(bar)/;

print  // 'x'; # (1-based index)
print  // 'x'; # (1-based index)

打印xbar

但是,对于 Raku,它的行为就像有一个 branch reset group 包裹了整个正则表达式:

'bar' ~~ m/(foo)|(bar)/;

print [=11=] // 'x'; # (0-based index)
print  // 'x'; # (0-based index)

打印barx

我可以接受这种行为:)。但是,有时了解哪个组在交替下被捕获是很有用的。

如何知道有乐的群?

有几种方法可以做到,实用程度各不相同。

一种方法是明确告诉 Raku 你想要的数字是什么:

'bar' ~~ m/=(foo)|=(bar)/;

如果您扩展正则表达式,计数将继续为 3 美元。

一种 less-recommendable 的方法是偷偷加入一组额外的括号:

'bar' ~~ m/(foo)|()(bar)/;

foo 将匹配 $0 中的第一个,$1 将是未定义的,而 bar 将匹配 $1 且 $0 为空(但不是未定义)。 TIMTOWTDI 但这不是一个好的 ;-)

另一种方法是使用标志:

 my $flag;
'bar' ~~ m/(foo {$flag = 'first'} ) | (bar {$flag = 'second'} )/;

标志将根据匹配设置。这实际上可以是一种 not-terrible 做事的方式,特别是如果你的标志是二进制的并且你会有一些逻辑你会 运行 超过它。

另一种类似的方法是利用通常在操作 类 中使用的 .make/.made,但仍然可以内联使用:

'bar' ~~ m/(foo {make 'first'} ) | (bar {make 'second'} )/;
say [=13=].made; # 'second'

如果您有很多元数据想要与之相关联,那么这个非常好(但如果只知道选择了哪个,可能有点过头了)。

有几件事会导致捕获索引重置。 ||| 恰好是一个。

将它放在另一个捕获组中是另一回事。 (因为匹配结果是一棵树。)


在设计 Raku 时,所有内容都经过重新设计,使其更一致、更有用且更强大。包括正则表达式。

如果你有这样的交替:

/  (foo)  |  (bar)  /

您可能想这样使用它:

$line ~~ /  (foo)  |  (bar)  /;
say %h{ ~[=11=] };

如果 (bar) 改为 </code>,则必须这样写:</p> <pre><code>$line ~~ / (foo) | (bar) /; say %h{ ~[=12=] || ~ };

捕获组编号从零重新开始通常更有用。

这也使得正则表达式更像是一种通用编程语言。 (每个“块”都是一个独立的子表达式。)


现在有时重新编号捕获组可能会很好。

/ ^
[   (..) '-'  (..) '-' (....)  # mm-dd-yyyy
|   (..) '-' (....)            # mm-yyyy
]
$ /

请注意 yyyy 部分是 </code> 或 <code> 取决于是否包含 dd 部分。

my $day   = + ??  !! 1;
my $month = +[=14=];
my $year  = + || +;

我们可以将 yyyy 重新编号为始终 </code>。</p> <pre><code>/ ^ [ (..) '-' (..) '-' (....) # mm-dd-yyyy | (..) '-' = (....) # mm-yyyy ] $ / my $day = + || 1; my $month = +[=15=]; my $year = +;

或者如果我们还需要接受 yyyy-mm-dd

怎么办
/ ^
[   (..) '-' (..) '-' (....)                # mm-dd-yyyy
|   (..) '-'  = (....)                    # mm-yyyy
|    = (....) '-' [=16=] = (..) '-'  = (..) # yyyy-mm-dd
]
$ /

my $day   = + || 1
my $month = +[=16=];
my $year  = +;

实际上现在我们有很多捕获组,让我们再看看如果 | 没有导致编号的捕获组从 [=32=][=34= 重新开始,我们将如何处理它]

/ ^
[   (..) '-' (..) '-' (....) # mm-dd-yyyy
|   (..) '-' (....)          # mm-yyyy
|   (....) '-' (..) '-' (..) # yyyy-mm-dd
]
$ /

my $day   = + || + ||   1;
my $month = +[=17=] || + || +;
my $year  = + || + || +;

不太好。
一方面,您必须确保正则表达式和 my $day 正确匹配。

快速计算捕获组,确保这些数字匹配正确的捕获组。


当然还有一个问题,即有名称的概念会被数字捕获。

所以我们应该改用名字。

/ ^
[   $<month> = (..) '-' $<day> = (..) '-' $<year> = (....) # mm-dd-yyyy
|   $<month> = (..) '-' $<year> = (....)                   # mm-yyyy
|   $<year> = (....) '-' $<month> = (..) '-' $<day> = (..) # yyyy-mm-dd
]
$ /

my $day   = +$<day> || 1;
my $month = +$<month>;
my $year  = +$<year>;

长话短说,我会这样做:

/ $<foo> = (foo)  |  $<bar> = (bar) /;


if $<foo> {
    …
} elsif $<bar> {
    …
}