2013-03-21 18 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