perl6 在正则表达式中否定多个单词及其字符的排列

perl6 Negating multiple words and permutations of their chars inside a regex

在正则表达式中执行多个单词的否定和组成这些单词的字符排列的最佳方式是什么?

例如:我不想

"zero dollar"
"roze dollar"
"eroz dollar"
"one dollar"
"noe dollar"
"oen dollar"

但我确实想要

"thousand dollar"
"million dollar"
"trillion dollar"

如果我写

not m/ [one | zero] \s dollar /

它不会匹配字符的排列,并且外部的 "not" 函数将使正则表达式匹配其他所有内容,例如 "big bang" 而没有正则表达式中的 "dollar"。

m/ <- [one] | [zero] > \s dollar/ # this is syntax error.

非常感谢!

lisprog

使用代码断言:

您可以匹配任何单词,然后使用 <!{ }> 断言拒绝 "one" 或 "zero":

排列的单词
say "two dollar" ~~ / :s ^ (\w+) <!{ [=10=].comb.sort.join eq "eno" | "eorz" }> dollar $ /;

使用before/after:

或者,您可以预先生成不允许的单词的所有排列,然后在正则表达式中使用 <!before ><!after > 断言拒绝它们:

my @disallowed = <one zero>.map(|*.comb.permutations)».join.unique;

say "two dollar" ~~ / :s ^ <!before @disallowed>\w+ dollar $ /;
say "two dollar" ~~ / :s ^ \w+<!after @disallowed> dollar $ /;

这是一个行之有效的解决方案。它使用一个 helper-sub is-bad-word$needle(即它在目标字符串中找到的内容)与 @badwords 进行比较,如果 any 匹配,它将 return 正确。

在正则表达式本身内部,我使用了一个否定代码断言,将匹配的 (\w+) 传递到辅助子程序中。

需要指出的一件重要事情:如果你没有正确地将 (\w+) 锚定到单词的开头(我这次选择了字符串的开头),它只会向前跳过一个字符,当它发现一个坏词并无论如何接受(除非坏词只有一个字符开头,比如 a dollar)。毕竟,零在您的 @badwords 中,但 ero 不在。

希望对您有所帮助!

my @badwords = <one zero yellow>;

my @parsefails = q:to/EOF/.lines;
    zero dollar
    roze dollar
    erzo dollar
    one dollar
    noe dollar
    oen dollar
    yellow dollar
    wolley dollar
    EOF

my @parsepasses = q:to/EOF/.lines;
    thousand dollar
    million dollar
    dog dollar
    top dollar
    meme dollar
    EOF

sub is-bad-word($needle) {
    return $needle.comb.sort eq any(@badwords).comb.sort
}

use Test;
plan @parsefails + @parsepasses;

for flat (@parsefails X False), (@parsepasses X True) -> $line, $should-pass {
    my $succ = so $line ~~ / ^ (\w+) \s <!{ is-bad-word([=10=].Str) }> 'dollar' /;
    ok $succ eqv $should-pass, "$line -> $should-pass";
}

done-testing;