perl6 正则表达式匹配连词 &&

perl6 Regex match conjunction &&

Perl6 正则表达式匹配连词 && returns 如果连词中的所有部分匹配相同的子字符串而不是整个字符串则为真:

> 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”而不是 "the part of $a that matches 23 or matches ef":

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

谢谢!

lisprog

如果 $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;
    }
}

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

您可以使用两个正则表达式中的 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

专注于简单而非速度的解决方案

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

(主要语言的 & 运算符是 Junction 运算符。联结是具有两个关键特征的连词;一个是它们的句法 brevity/simplicity/clarity;另一个是它们的并行处理语义。 )

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

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> ) ;

直觉

it is easier to interpret $a~~m/23&&ef/ as "$a matches 23 and $a matches ef"

是也不是。

是的,从某种意义上说 "matches a and b";对于一般探索正则表达式的人来说,您的猜测是几个合理的猜测之一;并且,特别是,您的猜测显然是您 目前 认为最合适的那个 "easiest".

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

(我刚刚发明了 "iofo"。我用它来表示 "in our friendly opinion",ioho 的一个版本,它不仅真诚地谦虚而且张开双臂,让人联想到I/we 想象的一种观点可能有一天会被一些读者愉快地分享。)

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

对于您建议的阅读,我们有路口,如上所述:

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

&&在单场比赛中一样,iofo上面的英文读作“$a matches 23 and ef”是合适的,但这次是“$a matches 23 and $”的缩写a 匹配 ef”,如你所愿。

同时,在单个匹配中使用&&对应于other有用的连词意义,也就是说它是指匹配正则表达式原子它的左边和右边的正则表达式原子是同一个子字符串。

Iofo 一旦意识到并习惯了连词的这两种可能解释,这是一种非常直观的方法。

If I have n regexes and I want to see if all these n regexes match the same whole string rather than match the same substring part of the whole string, then what is the best way to write the perl6 expression?

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

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

或者如果你想花哨:

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

如果你真的想

how do I check if all my regexes match a string, even if not the same substring

你可以表达为

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

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

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