Perl模式与可选标记匹配

时间:2013-03-21 00:08:35

标签: perl permutation

我有一个这样的字符串:

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

其中:

  1. []内的所有内容都是可选的
  2. 和里面的值(.. | ..)是OR强制值
  3. 因此,上述字符串的可能结果如下:

    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 
    

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

    谢谢!

3 个答案:

答案 0 :(得分: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 :(得分:1)

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

您应该关注的是排列。您的模板字符串包含以下部分:

  1. “a”或“没有”
  2. “好”或“坏”
  3. “word”
  4. “为了好玩”或没什么
  5. 因此片段1和2有两种可能性,片段3只有一种,片段4有两种,给你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];
    

答案 2 :(得分:1)

我认为这是尝试使用Parse::RecDescent的机会。我不太了解这些事情,所以可能有更好的方法来编写语法。

解析器允许我生成要使用的短语集列表。然后,我将该组列表提供给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