【问题标题】:How to put a sub inside a regex in Perl 6?如何在 Perl 6 中将 sub 放入正则表达式中?
【发布时间】:2017-11-10 08:35:18
【问题描述】:

这就是我想要做的。

>  my sub nplus1($n) {$n +1}
> my regex nnplus1 { ^ (\d+) &nplus1($0) $ }
> "123" ~~ &nnplus1
P6opaque: no such attribute '$!pos' in type Match...

【问题讨论】:

  • 您可以尝试将 sub 放入代码块 my regex nnplus1 { (\d+) { &nplus1($0)} } 但它不会将结果从 123 更改为 124.. 我还不确定该怎么做。您的预期输出/结果是什么?
  • 这个很有趣,包括我认为应该被视为错误(但不是)的行为。我现在正在试验它。
  • 我希望它匹配“123124”。我已经在正则表达式中添加了锚点。
  • @EugeneBarsky 正确的版本可以,不需要任何额外的修改。
  • @EugeneBarsky 我发现 YAMLish 是高级语法/正则表达式技术的一个很好的例子。 rakudo 源代码本身还有另一个——有一个解析 Perl 6 的文件。YAMLish 是在线的,但 Leon 发表了关于它的演讲,我发现它给了我项目所需的想法。如果你搜索它,你可以在网上找到它。

标签: raku


【解决方案1】:

请记住,正则表达式是 subs。所以不要称你的匹配器为sub——更具体地称它为regex。是的,您可以将参数传递给regex/token/rule。当您匹配在解析时更改其状态的语言时,这样做非常重要。例如,在 YAML 中,您可以解析“data[0]: 17”。之后,下一行可以以“data[1]”开头,但不能以“data[2]”开头。因此,将额外信息作为参数传递是很有用的。

另请注意,当您将其转换为正则表达式时,有些事情会发生变化。 $n+1 将具有新的含义(这是错误的)。但是,仍然会插入简单变量,因此如果您在正则表达式主体中使用:my $npp = ... 将其声明为新变量。但即便如此,你会发现它仍然不起作用。当你添加一个像{say "n is $n"} 这样的帮助语句时,你会看到你没有得到一个有效的参数。这是因为在没有大括号的类似代码的上下文中(当您使用表达式作为另一个匹配器的参数时),rakudo 不会更新匹配变量。添加大括号时,将重新计算或重新缓存当前匹配变量。这个 hack 看起来像是一个错字,所以我建议你添加一个解释空括号的注释。最后的代码是这样的:

my regex nplus1($n) {
 :my $npp=$n+1;
 $npp
}
my regex nnplus1 { (\d+) {} <nplus1($0)> }
say "123124" ~~ &nnplus1;

在这种情况下(基本上是递归),我喜欢通过更改参数中的数据而不是更改函数体中的数据来保持简洁:&lt;nplus1($0+1)&gt; 而不是定义 :my $npp = $n+1;

【讨论】:

  • 我刚刚测试过它,它似乎不起作用(或者我不明白一些重要的东西)。在这两个匹配中,第一个捕获获取整个字符串,nplus1 =&gt; 「」.
  • @EugeneBarsky 我明白了。是的,这似乎很奇怪。我认为$0 应该是 123,$&lt;nplus1&gt; 应该是 124。同意吗?我会看看我能不能完成这项工作......
  • 是的,这就是我想要的。并且有 123 个字符串,$0 应该是 1,nplus1($0) 应该是 2(如果我们不添加锚点)。
  • @EugeneBarsky 我第一次犯了一个大错误,但是通过几个简单的更改它就可以了。在正则表达式中用{} 插入字符串是错误的,至少在这种情况下是这样。查看更新的答案,但您需要将 $n+1 放入一个新变量中,以便将其插入到正则表达式中。
  • @EugeneBarsky 除了语法之外,这可能是哲学上的差异。有人可能会争辩说,最好使用帮助器 sub 来执行逻辑,并使用 regex 来完成最终匹配。但可能需要更多的语法经验才能了解最佳实践。
【解决方案2】:

&lt;{...}&gt; 构造在正则表达式中运行 Perl 6 代码,并将结果作为正则表达式求值:

my sub nplus1($n) {$n +1} my regex nnplus1 { ^ (\d+) <{ nplus1($0) }> $ } say so '23' ~~ &nnplus1; # Output: True say so '22' ~~ &nnplus1; # Output: False

【讨论】:

    【解决方案3】:

    根据Regex interpolation 文档以及piojo's answer 和Håkon Hægland 的评论,看来我已经做到了我想要的:

    my sub nplus1($n) {
     $n+1;
    }
    my regex nnplus1 { (\d+) {} <nplus1=$(nplus1($0))> }
    say "123124" ~~ &nnplus1;
    

    输出:

    「123124」
     0 => 「123」
     nplus1 => 「124」
    

    或者我们可以移动 {} 来包围插入的​​子:

    my sub nplus1($n) {
     $n+1;
    }
    my regex nnplus1 { (\d+)  <nplus1={nplus1($0)}> }
    say "123124" ~~ &nnplus1;
    

    (输出将相同)

    【讨论】:

    • 这很有趣。一方面,我不明白你为什么要那样做。这可能在语义上与我的答案非常相似,尽管这是我没想过的语法。另一方面,我看不出有任何理由不这样做。我想这取决于nplus1 的内容。该函数是否进行真正的计算、设置状态并引起副作用?如果是这样,那么是的,将其称为sub 是有意义的。就我而言,当我需要接受参数的匹配器时,匹配器通常只需要接受不同长度的缩进,因此我将其称为子。
    • 顺便说一句,我认为您在插值中多了一层$(),它似乎没有任何作用。
    • 我认为&lt;nplus1={nplus1($0)}&gt; 的结果与&lt;nplus1={$(nplus1($0))}&gt; 相同。
    • 另一个目标是匹配&lt;pattern1&gt; &lt;pattern2&gt;,其中pattern2 取决于pattern1 的精确匹配。这种依赖可能比较复杂,写在正则表达式中会很不方便。
    • 你严重误解了rule!在使用regextoken 声明符声明的规则中,空格(未加引号的)并不重要。把它们放进去,把它们拿出来,匹配保持不变。所以regex { foobar }token { fo ob ar } 匹配完全相同的字符串。相反,在用rule 声明的规则中,原子后的空格很重要。事实上,这是ruletoken 的唯一区别。 rule 中原子后的空格会自动替换为匹配零个或多个空格字符的&lt;.ws&gt;
    猜你喜欢
    • 1970-01-01
    • 2017-04-14
    • 1970-01-01
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-23
    • 1970-01-01
    相关资源
    最近更新 更多