如何在 Raku 中将包含捕获组括号的字符串插值为正则表达式?

11

我想要匹配一个包含多个(.*)捕获组的通过程序构建的正则表达式。我将这个正则表达式作为一个字符串保存,比如说

my $rx = "(.*)a(.*)b(.*)"

我想将该字符串作为正则表达式进行插值并进行匹配。 文档 告诉我应该使用 <$rx>(即将该字符串作为正则表达式插值)来实现,但它并没有成功。请比较匹配的输出(在perl6 REPL中):

> 'xaybz' ~~ rx/<$rx>/
「xaybz」

与期望/期望的输出相比,将捕获组设置为分开的:

> 'xaybz' ~~ rx/(.*)a(.*)b(.*)/
「xaybz」
 0 => 「x」
 1 => 「y」
 2 => 「z」

评论

我可以通过 EVAL 正则表达式匹配(也可以在 REPL 中执行)的方式来实现,但这种方式不太吸引人。

> use MONKEY; EVAL "'xaybz' ~~ rx/$rx/";
「xaybz」
 0 => 「x」
 1 => 「y」
 2 => 「z」

所以虽然这给了我一个解决方案,但我相信我错过了一个字符串插值的技巧,可以避免依赖于 EVAL

3个回答

10
做匹配的结果是在超出正则表达式范围时进行匹配。这将起作用:

做匹配的结果是在超出正则表达式范围时进行匹配。这将起作用:

my $rx = '(.*)a(.*)b(.*)';
'xaybz' ~~ rx/$<result>=<$rx>/;
say $<result>;
# OUTPUT: «「xaybz」␤ 0 => 「x」␤ 1 => 「y」␤ 2 => 「z」␤»

由于通过分配给Match变量,您正在访问原始的Match,然后可以打印它。问题是<$rx>实际上是一个Match,而不是一个字符串。因此,您正在执行匹配一个Match的正则表达式。可能会将Match转换为字符串,然后进行匹配。这是我能够解释结果的最接近方式。


1
非常感谢,但我不认为我真正理解发生了什么。您能告诉我这在哪里有记录吗? - grobber
让我困惑的是,“字符串化”似乎不是正在发生的事情。请注意结果周围的引号 「 」。如果我使用 $/.Str 将匹配项字符串化,那么这些引号就会消失。因此,我的获取 「xaybz」 不能归因于“字符串化”,因为它仍然是一个匹配对象。问题似乎在于我的初始尝试中忽略了捕获括号,我不知道为什么。 - grobber
@grobber 没错。它只是被转换成了另一个Match对象。我会稍微编辑一下,试着解释一下正在发生的事情。基本上,<$rx>是一个Match,而你正在将其与一个Match匹配,这个Match被字符串化了... - jjmerelo

9
问题在于一般情况下,位于<...>中的内容不会被捕获。
'xaybz' ~~ / <:Ll> <:Ll> <:Ll> /
# 「xay」

它们会捕获如果<后面的第一件事是字母。

my regex foo { (.*)a(.*)b(.*) }

'xaybz' ~~ / <foo> /;
# 「xaybza」
#  foo => 「xaybza」
#   0 => 「x」
#   1 => 「y」
#   2 => 「za」

如果您使用<a=…>,同样适用。
'xaybz' ~~ / <rx=$rx> /;
# 「xaybza」
#  rx => 「xaybza」
#   0 => 「x」
#   1 => 「y」
#   2 => 「za」

当然你也可以在外部分配它。

'xaybz' ~~ / $<rx> = <$rx> /;
# 「xaybza」
#  rx => 「xaybza」
#   0 => 「x」
#   1 => 「y」
#   2 => 「za」

'xaybz' ~~ / $0 = <$rx> /;
# 「xaybza」
#  0 => 「xaybza」
#   0 => 「x」
#   1 => 「y」
#   2 => 「za」

请注意,<...>是一个子匹配项,所以$rx$0$1$2永远不会在顶层。

感谢您的解释。特别是关于非字母<>的部分很有启发性。请注意,问题会传播:如果我先执行my $rx='(\d)'; my regex R { <$rx> }; '12' ~~ /<R>/,它仍然无法捕获。 - grobber
3
这是因为你将R定义为<$rx>。如果你使用regex R { <rx=$rx> },它就能正常工作。要求命名的原因之一是编译时计算位置捕获的数量。如果你使用regex R { <$rx> (.) },就不可能知道(.)的数字应该是多少。命名捕获不会受到相同的限制。 - user0721090601
2
请注意问题会传播。我对你选择使用“问题”这个词感到困惑。只有以字母开头的<...>断言才能捕获,这是一个故意设计的特性。在你的例子中...啊,@user0721090601已经解决了这个问题。 :) - raiph
1
@raiph:只是从我之前不了解的角度来看一个“问题”。谢谢你们两个(你自己和@user0721090601):现在要求命名<>的动机已经清楚了。 - grobber

1
你可以通过以下方式将内部正则表达式结果暴露给外部变量:
my $rx = "(.*)a(.*)b(.*)";
my $result;

'xaybz' ~~ / $<result>=<$rx> {$result = $<result>}/;

say $result;

# OUTPUT:

# 「xaybz」
# 0 => 「x」
# 1 => 「y」
# 2 => 「z」

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接