【问题标题】:perl6 Regex match conjunction &&perl6 正则表达式匹配连词 &&
【发布时间】:2018-12-16 05:22:55
【问题描述】:

Perl6 正则表达式匹配连接 && 如果连接中的所有部分匹配相同的子字符串而不是整个字符串,则返回 True:

> my $a="123abc456def";
123abc456def
> so $a ~~ m/ 23 && ef /
False

这是 False 因为连词中的“23”匹配了 $a 中的“23”子串,但是这个子串不匹配连词中的“ef”。这有点违反直觉,因为将 $a~~m/23&&ef/ 解释为“$a 匹配 23 并且 $a 匹配 ef”比解释为“$a 有一个匹配 23 的子字符串并且这个子字符串也匹配 ef”更容易”。

如果我有 n 个正则表达式,并且我想查看所有这些 n 个正则表达式是否匹配相同的整个字符串,而不是匹配整个字符串的相同子字符串部分,那么编写 perl6 表达式的最佳方法是什么?

在示例中,我是真的打算这样做

so (($a ~~ /23/) && ($a ~~ /ef/))

如果正则表达式的数量很大,那么除了循环之外,上面的代码更难编写:

so (gather {for @myRegexes { take $a ~~ / $_ /; } }).all

有没有更简单的方法?

通过交替,更容易理解为“$a 匹配 23 或 $a 匹配 ef”而不是“$a 匹配 23 或匹配 ef 的部分”:

> so $a ~~ m/ 23 || ef /
True

谢谢!

lisprog

【问题讨论】:

标签: regex match raku


【解决方案1】:

您可以使用两个正则表达式中的一个Junction,以便只提及一次$a

my $a = 'abcdef12345'; say so $a ~~ /23/ & /ef/   # True
my $a = 'abcde12345'; say so $a ~~ /23/ & /ef/    # False 
my $a = 'abcdef1245'; say so $a ~~ /23/ & /ef/    # False

要从正则表达式数组形成联结,请在数组上调用.all

如果它真的只是要查找的文字字符串,那么 contains 的运行速度可能会快很多:

my $a = 'abcdef12345'; say so $a.contains(all('23', 'ef'))   # True
my $a = 'abcde12345'; say so $a.contains(all('23', 'ef'))    # False
my $a = 'abcdef1245'; say so $a.contains(all('23', 'ef'))    # False

【讨论】:

    【解决方案2】:

    注重简单性而非速度的解决方案

    暂时忽略正则表达式,用于缩短 foo op bar and foo op baz 的通用 P6 构造,前提是 op 是纯粹的,因为可以并行运行多个调用,是 foo op bar & baz

    (主要语言的& 运算符是Junction 运算符。连接是具有两个关键特征的连词;一个是它们的语法简洁/简单/清晰;另一个是它们的并行处理语义。)

    将此应用于您的正则表达式匹配中的~~ op:

    my $a="123abc456def";
    say so $a ~~ / 23 / & / ef /
    

    如果bar & baz & ... 很好地放在一行中,上述内容通常是合适的。

    仍然使用联结逻辑但跳过操作数之间的中缀运算符并更好地扩展到更大的模式列表以匹配的替代方法是:

    my @keywords = <12 de>;
    say so all ( $a.match: / $_ / for @keywords ) ;
    

    (感谢@lisprogtor 发现并耐心解释了我的原始代码中的错误。)

    注重速度而非简单性的解决方案

    有很多方法可以优化速度。我只提供一个。

    如果您的所有或大部分模式只是字符串而不是正则表达式,则使用the .contains method 而不是正则表达式作为字符串:

    say so all ( $a.contains: $_ for <23 ef> ) ;
    

    直觉

    $a~~m/23&amp;&amp;ef/ 更容易解释为“$a 匹配 23,$a 匹配 ef”

    是和不是。

    是的,从某种意义上说,“匹配 a 和 b”存在歧义;对于任何探索正则表达式的人来说,你的猜测是几个合理的猜测之一;并且,特别是,您的猜测显然是您当前认为最合适的,也就是“最简单的”。

    不,如果我们的 iofo 匹配的话。

    (我刚刚发明了“iofo”。我用它来表示“在我们的友好意见中”,ioho 的一个版本,它不仅真诚地意图谦虚,而且张开双臂,让人联想到我/我们想象有一天可能会被一些读者愉快地分享。)

    Iofo 我们发现将$a~~m/23&amp;&amp;ef/ 理解为“$a 匹配 23 和 ef”而不是“$a 匹配 23 和 $a 匹配 ef”更容易。但是当然,“$a 匹配 23 和 ef”仍然是模棱两可的。

    对于您建议的阅读,我们有交叉点,如上所述:

    say so $a ~~ / 23 / & / ef /
    

    就像&amp;&amp; 在单个匹配中一样,iofo 将上面的英文读作“$a 匹配 23 和 ef”是合适的,但这次它是“$a 匹配 23 和 $a 匹配 ef”的缩写,如你所愿。

    同时,在单个匹配中使用&amp;&amp;对应于其他有用的连词含义,也就是说,它指的是匹配其左侧的正则表达式原子和正则表达式原子它对相同子字符串的权利。

    一旦人们意识到并习惯于对连词的这两种可能的解释,Iofo 这是一种高度直观的方法。

    【讨论】:

    • 非常感谢 raiph !您的帮助使我能够更好地编写代码。我需要将一个文件放入一行,然后检查该文件是否包含许多关键字。谢谢!!
    • 嗨 raiph:我测试了你的代码 { 这么说 $a ~~ (m/ $_ / for ).all; } 但需要做一个小改动,因为在 m// 中的 $_ 实际上是指 $a 而不是 的元素;简单的解决方法是这样的:say $a ~~ ( for -> $k { m/ {say "$_ vs $k";} $k / }).all;再次感谢!
    • 谢谢你。我使用最新的 Rakudo 2018.04.1,MoarVM。在您的代码中, $_ 设置为字符串,而不是“for”语句中的项目。这是输出: > say so "12ab" ~~ (m/$_/ for ).all;输出为真; > 这么说 "12ab" ~~ (m/$_/ for ).all;输出也为真;那是因为 $_ 匹配自己。如果您在系统上得到不同的结果,请告诉我。谢谢。
    • 啊,呃,不,我的代码是错误的,但出于相反的原因。 $_ is 设置为“for”语句中的项目——而 that 就是问题所在。它正在检查“for”语句中的每个项目是否与自身匹配!唔。我将按照您最初的建议编辑我的答案。 :)
    【解决方案3】:

    如果$a 字符串很长,您可以尝试通过避免为每个子字符串从字符串开头重新开始来减少运行时间:

    my $a="123abc456def23";
    my %pats = <23 ef>.map({ $_ => 1 });
    my $join = %pats.keys.join('|');
    my $rx = rx{ <{$join}> };
    for $a ~~ m:g/ $rx / -> $match {
        %pats{$match.Str}:delete;
        if %pats.keys.elems == 0 {
            say "Match!";
            last;
        }
    }
    

    当然,这不会使代码更短(意思是更优雅),但它可以减少运行时间。

    【讨论】:

    • 请注意,此解决方案可以与 Perl 6 版本的 Perl 5 模块 Regexp::Assemble 结合使用,以进一步减少运行时间
    【解决方案4】:

    如果我有 n 个正则表达式,并且我想查看所有这些 n 个正则表达式是否匹配相同的整个字符串,而不是匹配整个字符串的相同子字符串部分,那么编写 perl6 表达式的最佳方法是什么?

    这是一个内正则表达式解决方案:

    / ^ [ $re1 && $re2 && $re3 ] $ /
    

    或者如果你想变得花哨:

    / [^ .* $ ] && $re1 && $re2 /
    

    如果你真的想

    如何检查我的所有正则表达式是否匹配一个字符串,即使不是相同的子字符串

    你可以这样表达

    / .* $re1 .* && .* $re2 .* && .* $re2 .* /
    

    为避免过度回溯,您应该锚定整个正则表达式:

    / ^ [ .* $re1 .* && .* $re2 .* && .* $re2 .* ] $ /
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-04
      • 2015-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多