为啥我使用这些 Raku 正则表达式会得到不同的回溯?
Posted
技术标签:
【中文标题】为啥我使用这些 Raku 正则表达式会得到不同的回溯?【英文标题】:Why do I get different backtracking with these Raku regexes?为什么我使用这些 Raku 正则表达式会得到不同的回溯? 【发布时间】:2020-12-09 23:13:56 【问题描述】:我意外地回溯了 Raku 正则表达式的 +
量词。
在这个正则表达式中:
'abc' ~~ m/(\w+) say $0 <? $0.substr(*-1) eq 'b' >/;
say $0;
我得到了预期的结果:
「abc」 # inner say
「ab」 # inner say
「ab」 # final say
也就是说,(贪婪的)+
量词获取所有字母,然后条件失败。之后,它通过释放最后一个得到的字母开始回溯,直到条件评估为真。
但是,当我将量词放在捕获组之外时,回溯的工作方式似乎不同:
'abc' ~~ m/[(\w)]+ say $0 <? $0.tail eq 'b' >/;
say $0;
结果:
[「a」 「b」 「c」] # inner say
[「a」 「b」 「c」] # why this extra inner say? Shouldn't this backtrack to [「a」 「b」]?
[「a」 「b」 「c」] # why this extra inner say? Shouldn't this backtrack to [「a」 「b」]?
[「b」 「c」] # Since we could not successfully backtrack, We go on matching by increasing the position
[「b」 「c」] # Previous conditional fails. We get this extra inner say
[「c」] # Since we could not successfully backtrack, We go on matching by increasing the position
Nil # final say, no match because we could not find a final 'b'
这是预期的行为吗?如果是这样:为什么它们的工作方式不同?是否可以模仿第一个正则表达式但仍将量词保留在捕获组之外?
注意:
使用惰性量词“解决”问题...这是意料之中的,因为差异似乎发生在回溯中,而惰性量词不会发生这种情况。
'abc' ~~ m/[(\w)]+? say $0 <? $0.tail eq 'b' >/;
[「a」]
[「a」 「b」]
[「a」 「b」]
但是出于性能原因,我宁愿使用贪婪的量词(这个问题中的例子是一个简化)。
【问题讨论】:
在你的第二个例子中,我不明白方括号的使用。如果我将方括号放在括号内,我会得到与单独使用括号相同的结果,即([\w+])
和 ([\w])+
模仿它们的非方括号包含对应项。
@jubilatious1 是的,括号没用:)。原始的正则表达式更复杂,我开始剥离它的一部分以获得最简单的情况。我终于忘记去掉括号了。
【参考方案1】:
我认为问题不在于回溯。但是看起来中间的$0
暴露了保留了之前的迭代捕获。考虑这个表达式,
'abc' ~~ m/[(\w)]+ say "Match:",$/.Str,";\tCapture:",$0 <? False >/;
这是输出:
Match:abc; Capture:[「a」 「b」 「c」]
Match:ab; Capture:[「a」 「b」 「c」]
Match:a; Capture:[「a」 「b」 「c」]
Match:bc; Capture:[「b」 「c」]
Match:b; Capture:[「b」 「c」]
Match:c; Capture:[「c」]
如您所见,匹配顺序正确,abc ab a ...
。但是ab
匹配的捕获数组也是[「a」 「b」 「c」]
。我怀疑这是一个错误。
对于您的情况,有几种方法。
-
只需使用
$/
进行条件检查
'abc' ~~ m/[(\w)]+ <? $/.Str.substr(*-1) eq 'b' >/;
或者,另外还使用限定词捕获组。
'abc' ~~ m/([(\w)]+) <? $0[0][*-1] eq 'b' >/;
这里$0
匹配外部组,$0[0]
匹配第一个内部组,$[0][*-1]
匹配本次迭代中最终匹配的字符。
【讨论】:
比$/.Str.substr(*-1) eq 'b'
更容易的是$/.ends-with: 'b'
嗨!您用额外的捕获组包装量化器的解决方法就像一个魅力!让我们看看 rakudo 的人怎么说这是一个错误
我已经提交了一个问题With (foo)+
the corresponding sub-captures aren't removed during backtracking。
@raiph,谢谢
@jubilatious1,给出的正则表达式,是用于演示问题的简化表达式。根据演示输出,希望您同意存在差异。是的,只有当我们使用回溯和$0
时才会出现问题。以上是关于为啥我使用这些 Raku 正则表达式会得到不同的回溯?的主要内容,如果未能解决你的问题,请参考以下文章
notepad++匹配汉字的正则表达式与Java中的为啥不同?