如何让 & 联合正则表达式被视为与其组成部分一样长的匹配项?

dar*_*rch 8 regex raku

您可以使用连词&&来指定正则表达式仅在其所有组成部分都匹配时才匹配&

\n
[4] >  \'ready\' ~~ / r..dy & .ea..  /\n\xef\xbd\xa2ready\xef\xbd\xa3\n[5] >  \'roody\' ~~ / r..dy & .ea..  /\nNil\n[6] >  \'peaty\' ~~ / r..dy & .ea..  /\nNil\n
Run Code Online (Sandbox Code Playgroud)\n

当选择交替匹配的哪一方时,Raku 选择“更好”的一方。这里有两条规则很重要:具有较长声明性前缀的替代方案比具有较短声明性前缀的替代方案更好,并且较早声明的替代方案比其同级方案更好。

\n

&文档表明使用代替的部分原因&&是被&认为是声明性的。如果我正确理解最长的令牌匹配,那就是我想要的。

\n

然而,如果我使用连接作为交替的一个分支,就会发生一些令人惊讶的事情:如果它的替代项没有连接,则永远不会选择它。它似乎被认为比在我看来相同长度的火柴短。

\n

这很烦人,因为我正在编写一个解析器,在其中很自然地说“如果文本与这两个规则都匹配,则它实际上被认为是联合规则的示例”。语法始终倾向于寻找构成规则而不是联合规则。

\n

以下 REPL 示例都是模式的变体,其中我们使用联合替代方案和非联合替代方案进行交替,我们想知道为什么选择非联合替代方案:

\n
\'froody\' ~~ / froody & froody | froody /\n
Run Code Online (Sandbox Code Playgroud)\n

(REPL 交互具有诊断代码和额外的括号,以确保我不会遇到优先级问题。)

\n

在这里,交替的两侧在我看来应该被认为是相同的长度,所以我希望它选择左边的分支。它选择正确的分支。

\n
[7] >  \'froody\' ~~ / [ [ froody & froody ] { say \'left\' } ] | [ froody { say \'right\' } ] /\nright\n
Run Code Online (Sandbox Code Playgroud)\n

如果我颠倒顺序,它仍然选择非联合分支。

\n
[7] >  \'froody\' ~~ / [ froody { say \'left\' } | [ [ froody & froody ] { say \'right\' } ] ] /\nleft\n
Run Code Online (Sandbox Code Playgroud)\n

如果我通过 prepending 人为地缩短非联合分支的声明部分{},它会选择左分支......

\n
[7] >  \'froody\' ~~ / [ [ froody & froody ] { say \'left\' } ] | [ {}froody { say \'right\' } ] /\nleft\n
Run Code Online (Sandbox Code Playgroud)\n

...以及如果我们翻转它们。这表明联合分支被认为具有 0 的声明长度。

\n
[7] >  \'froody\' ~~ / [ {}froody { say \'left\' } | [ [ froody & froody ] { say \'right\' } ] ] /\nleft\n
Run Code Online (Sandbox Code Playgroud)\n

那么:如何让带有连词的交替被认为包含与非连体替代一样长的匹配,而不需要愚蠢的黑客?这是一个不合理的愿望吗?是&不是应该可以做到这一点?

\n

rai*_*iph 6

免责声明:这个答案似乎是错误的。也就是说,它是经过仔细研究的,我认为它至少是对了一半。

\n

TL;DR某些正则表达式构造会终止模式的 LDP(最长声明前缀)。&并且[...]两者都在它们生成的子表达式的开头执行此操作。其他人则不这样做,包括&&(以及诸如 之类的断言<froody>),因此请使用它们。

\n

示例和讨论

\n

我从第一个例子的变体开始。这段代码...

\n
say \'foo\' ~~ /  foo & foo {print \'L \'}  |  foo {print \'R \'}  /\n
Run Code Online (Sandbox Code Playgroud)\n

...显示R \xef\xbd\xa2foo\xef\xbd\xa3。换句话说,这与您的第一个示例具有完全相同的行为,选择 RHS 分支而不是所需/预期的左分支。

\n

至关重要的是,我没有引入[...]子分组,这样就避免了悄然引入双重麻烦。(在我的测试中,& [...](和(...))都在自民党开始时终止。)

\n

现在我们可以将其更改&&&...

\n
say \'foo\' ~~ /  foo && foo {print \'L \'}  |  foo {print \'R \'}  /\n
Run Code Online (Sandbox Code Playgroud)\n

...并获得所需的结果:L \xef\xbd\xa2foo\xef\xbd\xa3

\n

“声明式”

\n

“声明性”通常意味着“表达计算的逻辑而不描述其控制流”。在 Raku 当前的正则表达式功能集中,它有两个与此处相关的特定含义:

\n
    \n
  1. “声明性”一词的几乎所有用法都指的是与|交替相关的“最长的声明性前缀”。

    \n
  2. \n
  3. “声明性”一词的一种用法所指的是,即&处理表达式的 LHS 和 RHS 的(缺乏指定的)顺序。

    \n
  4. \n
\n

来自文档

\n

推测性设计文档 S05大量使用“声明式”一词:

\n
\n

虽然语法为|没有改变,但默认语义略有改变。我们正在尝试将声明性匹配和过程性匹配混合在一起,以便我们能够充分利用两者。简而言之,您不需要为语法编写自己的分词器,因为 [Raku] 会为您编写一个。请参阅下面有关“最长令牌匹配”的部分。

\n

...

\n

与析取|和一样||,连词也有&和两种&&形式。该&形式被认为是声明性的而非程序性的;它允许编译器和/或运行时系统决定首先评估哪些部分,并且假设任一顺序一致发生是错误的。

\n
\n
\n

同样,在 Raku 文档中,特别是正则表达式文档中,我们发现:

\n
\n

简而言之,|这是什么...选择具有最长声明性前缀的分支。...有关更多详细信息,请参阅 LTM 策略。

\n

...

\n

&(与&&) 不同,被认为是声明性的,并且理论上所有段都可以并行计算,或者按编译器选择的任何顺序计算。

\n
\n

同样,在所有情况下,术语“声明性”都是合适的。但它在“最长的声明性前缀”这句话中的含义与“该形式被认为是声明性的”中的含义完全无关&

\n

的 LHS&&可以指定声明性前缀;为什么不&

\n

正如上一节所解释的,与 相关的“声明性”一词&不应被理解为暗示它必然从其 LHS(和 RHS?)中派生出声明性前缀(或它们对?)。此外,&在当前的 Rakudo 中,显然有一个零长度前缀。我在 2005 年之后的推测文档和 IRC 讨论中没有发现任何表明&LTM 意义上声明性前缀有任何贡献的意图。

\n

但为什么不通过凑合来扫除所有这些混乱&呢?&&在 LTM 游戏中表现出色

\n

我目前的想法是,那是因为&在总是将其留给编译器尝试匹配其 LHS 和 RHS 的顺序的意义上,就不会是声明性的。事实上,它永远不会将其留给编译器,因为 Raku(do) 无法知道表达式&最终是否会在 LTM 交替的上下文中动态出现,并且有时无法按照它喜欢的任何顺序执行此操作当它看到& 词法上的,而其他时候首先尝试 LHS 因为它动态地看到它看到它,因为这意味着重构可能会改变行为。

\n

因此&必须始终做与 完全相同的事情&&。但如果是这样的话,为什么还要提供它呢?拥有它的一个原因是因为可以选择以&声明方式声明一对,即让编译器可以自由地决定匹配它们的顺序。但在这种情况下,LTM 前缀&必须始终长度为零。

\n

  • 可怕的是,我怀疑事情可能是这样的,但我的测试并没有得出结论,我决定我是在跳影子。我会花点时间做饭,看看我的想法——谢谢你的帮助! (2认同)
  • 至于提出一个问题,我认为这已经足够令人惊讶了。文本中“声明性”的使用完全合理,但在上下文中确实令人困惑。更新文档以重写该段落对我来说似乎是合适的。 (2认同)