当使用单个`^`或`$`时,trans函数将永远挂起

时间:2019-08-26 15:24:58

标签: raku

trans方法与诸如/^/之类的正则表达式一起使用时,它将挂起并且无法再返回!

for (-9, -6 ... 0, 2 , 4 ... 10).rotor( 2 => -1) {
    .join(',').trans(/^/ => '[', /$/ => ')' ).say;
}

我对它进行了打印,以打印出以下内容:

[-9,-6)
[-6,-3)
[-3,0)
[0,2)
[2,4)
[4,6)
[6,8)
[8,10)

但是它只是烂透了,似乎不再返回了。正如Raku文档所说,“用一个或多个字符替换一个或多个字符”。看来trans必须至少使用一个字符:

> '123abc345'.trans( /<?after 34> 5$/ => '-')
123abc34-
> '123abc345'.trans( /<?after 34> 5/ => '-')
123abc34-
> '123abc345'.trans( /<?after 345> $/ => '-')
123abc345

> '123abc345'.trans( /^ \d+ <( \w+ )> $/ => '-')
123-

1 个答案:

答案 0 :(得分:4)

我同意它必须使用一个字符。

来自a doc I've written on trans

  

如果左侧的匹配项之一为空字符串或正则表达式,并且没有其他匹配项在输入字符串的给定位置匹配,则.trans进入无限循环。

也来自该文档:

  

[此文档]可能是朝着更新官方文档和/或清理相关规范测试和/或功能迈出的一步。

虽然我发现trans的几个问题可能涉及错误,但以上是我在文档中确定的唯一内容。 (我在一个奇怪的地方提到了这个问题。确实,文档组织有点奇怪。我认为我当时对跨语言工作感到厌倦,并打算再次返回该语言以取得进一步的进展,但忘记了直到您问了一个问题。)

无论如何,我只是searched rakudo issues on GHrt,并且两个队列中都没有匹配的错误。

我认为,至少在trans根本不使用任何正则表达式并且仅有一个空的 string 匹配器导致无限循环的情况下,它值得一个错误报告。 (零长度匹配 regex 导致循环的情况可能是一个单独的错误,应该通过修改regex引擎代码来解决。我不确定。)

无论哪种方式,如果您要提交问题,请链接到此SO,以便人们也可以接触我的翻译文档。

探索您的示例的变体

让我们以正则表达式/^/开头-匹配字符串的开头-使用非trans构造来确认其是否正确:

my $foo = 'x';
say $foo ~~ s/^/end/; # 「」
say $foo;             # endx

因此/^/匹配;这是零长度的匹配/捕获; s///构造将插入替换字符串。一切似乎都很好。


trans的行为要复杂得多。这是一个严重的“工具化”示例,其匹配模式与您的示例非常接近,并且也是trans的一部分:

sub start-regex ($which,$/) {
  say "start regex $which, ++count = {++$count}, .pos = {$/.pos}"
}
sub end-regex ($which,$/) {
  say "end regex $which, .pos = {$/.pos}, matched = '$/' \n"
}
sub replace ($which,$/) {
  say "regex $which replaces $/ at .pos = $/.pos()"; $which
}

my $foo = 'x';
my $count;

say $foo.trans:
  / { start-regex 1, $/ } ^                     { end-regex 1, $/ } /
     => { replace 1, $/ },
  / { start-regex 2, $/ } . <?{ $count > 0 }> $ { end-regex 2, $/ } /
     => { replace 2, $/ }

这将显示:

start regex 1, ++count = 1, .pos = 0
end regex 1, .pos = 0, matched = '' 

start regex 2, ++count = 2, .pos = 0
end regex 2, .pos = 1, matched = 'x' 

regex 2 replaces x at .pos = 1
start regex 2, ++count = 3, .pos = 0
end regex 2, .pos = 1, matched = 'x' 

start regex 1, ++count = 4, .pos = 1
start regex 2, ++count = 5, .pos = 1
2

这就是它的作用:

  • 调用并匹配第一个正则表达式。匹配长度为零。

  • 调用并匹配第二个正则表达式。匹配项是一个字符。

  • 确定第二个正则表达式更长,因此获胜。因此,请致电替换人员。

  • 将位置重置为零,并再次调用第二个正则表达式!我不知道为什么。它再次匹配,但是第二次替换是 not

  • 最后,现在位置增加了一个,它将再次尝试两个正则表达式,但是它们都无法匹配。


如果第二个正则表达式的条件从$count > 0更改为$count > 2,则情况会大不相同。它进入一个无限循环,开始于:

start regex 1, ++count = 1, .pos = 0
end regex 1, .pos = 0, matched = '' 

start regex 2, ++count = 2, .pos = 0
start regex 2, ++count = 3, .pos = 1
regex 1 replaces  at .pos = 0
start regex 1, ++count = 4, .pos = 0
end regex 1, .pos = 0, matched = '' 

start regex 1, ++count = 5, .pos = 0
end regex 1, .pos = 0, matched = '' 

regex 1 replaces  at .pos = 0
start regex 1, ++count = 6, .pos = 0

这就是它的作用:

  • 调用并匹配第一个正则表达式。匹配长度为零。

  • 调用第二个正则表达式。条件失败,所以它不会结束。

  • .pos重置为1(!?)并再次调用第二个正则表达式!我不知道为什么。它再次失败。

  • 调用与 1st 正则表达式对应的替换闭包。为什么?我以为trans的逻辑是不接受长度为零的正则表达式!

  • 位置未提前 ,然后再次匹配了第一个正则表达式。两次!并且没有尝试匹配第二个正则表达式。然后再次将与第一个正则表达式替换相对应的替换闭包称为

  • 现在我们陷入了循环,重复了最后一个要点!

非常奇怪的行为...