in reply to REgular expression to check the string that allows "a","b" and "c" to occur only once in any order.

I was going to create a nasty little test using length, tr///s, m//, and a couple of evals, but I just couldn't bring myself to complete it. Instead, I reached for CPAN and Math::Combinatorics.

use strict; use warnings; use Math::Combinatorics qw( permute ); use Test::More; my @match_wanted = qw( abc bca cab cba bac acb ); my @match_unwanted = qw( aab abbc acc ); my @charset = qw( a b c ); my %valid = map { join( '', @$_ ) => 1 } permute( @charset ); sub match { my $s = shift; return( exists $valid{$s} ? 1 : 0 ); } plan 'tests' => ( scalar @match_wanted + scalar @match_unwanted ); ok( match( $_ ), "matches '$_'" ) for @match_wanted; ok( ! match( $_ ), "does not match '$_'" ) for @match_unwanted;
Output:
1..9 ok 1 - matches 'abc' ok 2 - matches 'bca' ok 3 - matches 'cab' ok 4 - matches 'cba' ok 5 - matches 'bac' ok 6 - matches 'acb' ok 7 - does not match 'aab' ok 8 - does not match 'abbc' ok 9 - does not match 'acc'

Thanks and ++ to kyle for the framework.

  • Comment on Re: REgular expression to check the string that allows "a","b" and "c" to occur only once in any order. (permutations)
  • Select or Download Code

Replies are listed 'Best First'.
Re^2: REgular expression to check the string that allows "a","b" and "c" to occur only once in any order. (permutations)
by ikegami (Patriarch) on Dec 11, 2007 at 06:47 UTC

    It fails when you add adbc to @match_wanted. Easily fixed:

    use Math::Combinatorics qw( permute ); my @charset = qw( a b c ); my %valid = map { join( '', @$_ ) => 1 } permute( @charset ); my ($others) = map qr/[^$_]/, join '', map quotemeta, @charset; sub match { my $s = shift; $s =~ s/$others//g; return $valid{$s}; }

    or

    use Math::Combinatorics qw( permute ); my @charset = qw( a b c ); my ($valid) = map qr/^(?:$_)\z/, join '|', map quotemeta, map join('', + @$_), permute @charset; my ($others) = map qr/[^$_]/, join '', map quotemeta, @charset; sub match { my $s = shift; $s =~ s/$others//g; return $s =~ $valid; }

    or

    use Math::Combinatorics qw( permute ); use Regexp::List qw( ); my @charset = qw( a b c ); my ($valid) = map qr/^$_\z/, Regexp::List->new()->list2re(permute(@cha +rset)); my ($others) = map qr/[^$_]/, join '', map quotemeta, @charset; sub match { my $s = shift; $s =~ s/$others//g; return $s =~ $valid; }

      True. The OP's spec wasn't clear as to whether or not the strings could contain other characters (or were limited to only those in the specified character set), or if a, b, and c were really characters and not stand-ins for longer strings. I went with Occam's razor and took the OP literally. (Update: Since the list of invalid strings contained only a, b, and c; I assumed if other characters were possible there would be an example of it in the invalid list. ikegami is right, though - this was probably more of an assumption than I first thought.)

      This approach could also be problematic if the character set is large and/or if the wanted strings were significantly longer, since the number of permutations would rapidly increase. In that case, a RE-based approach may be better.

      Nonetheless, thanks for providing a more robust solution.

        I went with Occam's razor and took the OP literally.

        No, that's what I did. You added the clause "and no characters other than a, b and c".

        It could very well be the OP wants what you provided, but it's not a literal interpretation of the OP.

        The authors of the other two (working) solutions didn't add that clause.