jeanluca has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks

Is there a smart way to make the following if statment a little bit shorter:
if ( $a =~ /abcdefgh/ || $b =~ /abcdefgh/ || $c =~ /abcdefgh/ || ..... +.. ) { }
because each time the part between the two / / is the same, I get the feeling this can be done in a shorter way, right ?

Thanks
Luca

Replies are listed 'Best First'.
Re: howto make a very long IF statment shorter
by ikegami (Patriarch) on Jun 29, 2006 at 15:16 UTC
    foreach ($a, $b, $c) { next unless /abcdefgh/; ... last; }

    or

    if (grep /abcdefgh/, $a, $b, $c) { ... }

    or (Update)

    use List::MoreUtils qw( any ); if (any { /abcdefgh/ } $a, $b, $c) { ... }

    The grep solution is easier to read than the foreach solution, but it has the disadvantage of always executing the regexp for every item in the list. The foreach and the any solutions execute the regexp as few times as possible.

Re: howto make a very long IF statment shorter
by Fletch (Bishop) on Jun 29, 2006 at 15:16 UTC

    So long as the code inside the block doesn't depend on knowing which of the variables contained the match, just use grep.

    if( grep( /abcdefg/, $foo, $bar, $baz ) ) { ## ... }

    If you did need to know which one, then it gets more complicated. You could use a list of references (in which case you still wouldn't know which variable but you would have a means to diddle it), or you could stick things in a hash and use keys instead of variables.

Re: howto make a very long IF statment shorter
by Herkum (Parson) on Jun 29, 2006 at 15:51 UTC

    Are you looking for part of a string or the whole string? If you are looking for the whole string it is better to use eq because a regular expression is slower.

    if ( $a eq 'abcdefgh' or $b eq 'abcdefgh' or $c eq 'abcdefgh') { }

    Also shorter code does not necessary mean it is easier to read. Taking your code and putting it into a tabular format,

    if ( $a =~ /abcdefgh/ || $b =~ /abcdefgh/ || $c =~ /abcdefgh/ || ....... ) { }

    One more thing, if you are trying to match the same pattern, put the pattern in a string and match against it. Name the pattern something that will make sense for the program can also enhance readability.

    my $USER_NAME = qr{abcdefgh}xms; if ($a =~ $USER_NAME || $b =~ $USER_NAME || $c =~ $USER_NAME) { }
Re: howto make a very long IF statment shorter
by johngg (Canon) on Jun 29, 2006 at 21:05 UTC
    How about

    if ( q{abcdefgh} =~ /(?:$a|$b|$c)/ ) { # Do something }

    I've tested it briefly and it seems to work ok.

    Cheers,

    JohnGG

      No good. "b matches a" doesn't necessarily follow from "a matches b". In fact, when it does, you should be using eq, not a regexp.

      use strict; use warnings; my $var_a = ''; my $var_b = 'abc'; my $var_c = ''; if ( $var_a =~ /abcdefgh/ || $var_b =~ /abcdefgh/ || $var_c =~ /abcdefgh/ ) { print("orig: true\n"); } else { print("orig: false\n"); } if ( q{abcdefgh} =~ /(?:$var_a|$var_b|$var_c)/ ) { print("johngg: true\n"); } else { print("johngg: false\n"); }
      outputs
      orig: false johngg: true
        That's a pity. My testing was brief because I was about to leave for a long motorcycling weekend. I've had a look again on my return and it seems that my method would only work if I add anchors to the match. In that case it would only be equivalent to the exact match if ( $var_a =~ /^abcdefgh$/ || ... and not the more general match of the OP. Here is a modified version

        use strict; use warnings; my $w = q{}; my $x = q{}; my $y = q{}; my $z = q{}; cmpOr(); $x = q{abc}; cmpOr(); $z = q{abcdefghij}; cmpOr(); $z = q{abcdefgh}; cmpOr(); $y = q{abcdefgh}; cmpOr(); $z = q{abcdefghij}; cmpOr(); sub cmpOr { print qq{Comparing OR methods with:-\n}, qq{ \$w is ->$w<-\n}, qq{ \$x is ->$x<-\n}, qq{ \$y is ->$y<-\n}, qq{ \$z is ->$z<-\n}; if ($w =~ /abcdefgh/ || $x =~ /abcdefgh/ || $y =~ /abcdefgh/ || $z =~ /abcdefgh/) { print qq{ orig: true\n}; } else { print qq{ orig: false\n}; } if (q{abcdefgh} =~ /^(?:$w|$x|$y|$z)$/) { print qq{johngg: true\n}; } else { print qq{johngg: false\n}; } print qq{\n}; }

        When run this produces

        In the particular circumstance of wanting each variable to match the string exactly, this method could save a deal of typing but it's no use in the general case as you pointed out.

        Just out of curiosity I had a try at an alternative to if ( $var_a =~ /^abcde$/ && $var_b =~ /^abcde$/ && .... This is what I came up with.

        if (q{abcde} =~ /^(?=$var_a$)(?=$var_b$)(?=$var_c$)/)

        Cheers,

        JohnGG