2013-03-21 27 views
0

我有这样的字符串:Perl模式具有可选的令牌匹配

$words = "[a] (good|bad) word [for fun]"; 

其中:

  1. []内的一切是可选
  2. 内部
  3. 和值(.. | ..)是OR强制值

因此,上述字符串的可能结果如下所示:

a good word for fun 

a bad word for fun 

a good word 

a bad Word 

good word for fun 

bad word for fun 

good word 

bad word 

有人可以帮助我找到一种方法来提取所有可能的结果(如上面的例子)并将它们存储在一个数组中吗?

谢谢!

+1

为什么这个标签与正则表达式的标签?你不是要求一个正则表达式,而是要求排列组合。而且你让我浪费时间为此找到一个正则表达式。 – TLP 2013-03-21 00:44:14

回答

2
use warnings; 
use strict; 
use constant { OPT => 0, OR => 1, FIXED => 2 }; 

my $words = "[a] (good|bad) word [for fun]"; 
my @tokens; 
# parse input 
my @v = grep {$_} split /(\[|\]|\(|\||\))/, $words; 
while (my $token = shift @v) { 
    if ($token eq '[') { 
    push @tokens, [ OPT, shift @v ]; 
    shift @v; # ] 
    } elsif ($token eq '(') { 
    my @list; 
    do { 
     push (@list, [ FIXED, shift @v]); 
    } until (shift @v eq ')'); # '|,)' 
    push @tokens, [ OR, \@list ]; 
    } 
    else { 
    push @tokens, [FIXED, $token]; 
    } 
} 
# generate output 
my @phrases = (""); 
for my $token (@tokens) { 
    my @additions; 
    if ($token->[0] == OPT) { 
    push @additions, $_.$token->[1] for @phrases; 
    } elsif ($token->[0] == FIXED) { 
    $_ .= $token->[1] for @phrases; 
    } elsif ($token->[0] == OR) { 
    foreach my $list (@{$token->[1]}) { 
     push @additions, $_.$list->[1] for @phrases; 
    } 
    @phrases =(); 
    } 
    push @phrases, @additions; 
} 


print "$_\n" for map {s/^\s+//;s/[ ]+/ /g;$_} @phrases; 
1

使用正则表达式,您可以确定“坏词”是否与您的模式“[好] |好]”([作为正则表达式匹配,可能拼写为/(a)?(good|bad) word(for fun)?/])匹配。但是这听起来像是你真的想做相反的事情,也就是说。从您的模式中生成所有可能的输入。这不是正则表达式可以做到的。

你应该看到的是所谓的排列。你的模板字符串具有以下几个部分:

  1. “一” 或没有
  2. “好” 或 “坏”
  3. “字”
  4. “为了好玩” 或没有

所以片段1和片段2有两种可能性,一种片段是三种片段,另一种片段是两种片段,给出2 * 2 * 1 * 2 = 8的可能性。

只是将所有这些可能性存储在多维数组中,例如,

my $sentence = [["a ", ""], ["good", "bad"], ["word"], ["for fun", ""]]; 

然后在CPAN上查找排列算法或排列模块以查找所有组合。

my $badword = 
    $sentence->[0]->[0] 
    . $sentence->[1]->[1] 
    . $sentence->[2]->[0] 
    . $sentence->[3]->[0]; 
1

我认为这是一个机会,尝试使用Parse::RecDescent

至于单permuation一个例子,将被为代​​表的“坏词”。我不太了解这些东西,所以可能有更好的方法来编写语法。

解析器允许我生成一组要使用的短语列表。然后,我将这些集合列表提供给Set::CrossProduct以生成集合的笛卡尔积。

#!/usr/bin/env perl 

use strict; 
use warnings; 

use Parse::RecDescent; 
use Set::CrossProduct; 

our @list; 

my $parser = Parse::RecDescent->new(q{ 
    List: OptionalPhrase | 
      AlternatingMandatoryPhrases | 
      FixedPhrase 

    OptionalPhrase: 
     OptionalPhraseStart 
     OptionalPhraseContent 
     OptionalPhraseEnd 

    OptionalPhraseStart: /\\[/ 

    OptionalPhraseContent: /[^\\]]+/ 
     { 
      push @::list, [ $item[-1], '' ]; 
     } 

    OptionalPhraseEnd: /\\]/ 

    AlternatingMandatoryPhrases: 
     AlternatingMandatoryPhrasesStart 
     AlternatingMandatoryPhrasesContent 
     AlternatingMandatoryPhraseEnd 

    AlternatingMandatoryPhrasesStart: /\\(/ 

    AlternatingMandatoryPhrasesContent: /[^|)]+(?:[|][^|)]+)*/ 
     { 
      push @::list, [ split /[|]/, $item[-1] ]; 
     } 

    AlternatingMandatoryPhraseEnd: /\\)/ 

    FixedPhrase: /[^\\[\\]()]+/ 
     { 
      $item[-1] =~ s/\\A\\s+//; 
      $item[-1] =~ s/\s+\z//; 
      push @::list, [ $item[-1] ]; 
     } 
}); 

my $words = "[a] (good|bad) word [for fun]"; 

1 while defined $parser->List(\$words); 

my $iterator = Set::CrossProduct->new(\@list); 

while (my $next = $iterator->get) { 
    print join(' ', grep length, @$next), "\n"; 
} 

输出:

a good word for fun 
a good word 
a bad word for fun 
a bad word 
good word for fun 
good word 
bad word for fun 
bad word