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

Hi, I want to check whether a variable is equal to any one of the three (or even more) values. For example, I want to check whether $country equals to India, America, Germany or Austria. When I used if statement, in the following way, I did not get the output.

if($country eq (India|America|Germany|Austria)) { print "True"; }

Alternatively, if I use it the following way, it is working fine. But, this will practically be impossible for the number of options I am going to use in the script.

if($country eq "India"|$country eq "America"|$country eq "Germany"|$co +untry eq "Austria") { print "True"; }

If I use a array to store all the possible options, and check whether the variable $country matches to any of the elements, that too is working fine, but I am sure there will be a way of doing this in a simplified manner using if statement or any other conditional loops. Though it sounds simple, I have scratched my head enough. Can anyone help me please..

Replies are listed 'Best First'.
Re: Using the 'if' statement...
by ikegami (Patriarch) on Jul 09, 2008 at 09:22 UTC

    The second snippet is still wrong. Read up on "|" and "||" in perlop. You should be using the latter.

    On to your question:

    my @countries = ( "India", "America", "Germany", "Austria", ); if ( grep { $_ eq $country } @countries ) { print "True"; }

    Or a version that stops as soon as it finds a match:

    my @countries = ( "India", "America", "Germany", "Austria", ); for (@countries) { if ($_ eq $country) { print "True"; last; } }

    Or use a hash to avoid looping except during initialization:

    my @countries = ( "India", "America", "Germany", "Austria", ); my %countries = map { $_ => 1 } @countries; if ($countries{$country}) { print "True"; }
Re: Using the 'if' statement (with Benchmark)
by moritz (Cardinal) on Jul 09, 2008 at 10:08 UTC
    Use a hash, that scales well even for a very large number of comparisons:
    my %countries = ( India => 1, America => 1, Germany => 1, Austria => 1, ); if ($countries{$country}){ print "$country is indeed a country\n"; }

    Note that Perl 6 does support comparisons the way you wanted to do it, and they are called junctions. You could write that as

    use v6; # ensure we run Perl 6 my $country = 'Foo' if $country eq 'America' | 'India' | ... { say "True" } # or if you want to use an array: my @countries = <America India Germany Austria>; if $country eq any(@countries) { say "True" }

    Update: Here is a (perl 5) benchmark.

    # perl 5.8.8: Rate grep first regex hash grep 8219/s -- -17% -72% -100% first 9863/s 20% -- -67% -100% regex 29805/s 263% 202% -- -99% hash 3682290/s 44701% 37233% 12255% -- # perl 5.10.0: Rate grep first regex hash grep 8532/s -- -21% -80% -100% first 10778/s 26% -- -75% -100% regex 43115/s 405% 300% -- -99% hash 3429921/s 40099% 31723% 7855% --

    You can see that the trie optimization in 5.10.0 boost the performance of the regex method, but it doesn't change the overall order.

    With Perl 5.10.0 and later you can also use smartmatching, which is quite fast:

    sub smartmatch { die 'smart' unless 'Germany' ~~ @countries; die 'smart' if 'Foo' ~~ @countries; } __END__ Rate grep first smart regex hash grep 7587/s -- -17% -58% -83% -100% first 9135/s 20% -- -50% -79% -100% smart 18101/s 139% 98% -- -59% -99% regex 43840/s 478% 380% 142% -- -99% hash 3508785/s 46146% 38312% 19285% 7904% --

    Oh, and I forgot to mention: TIMTOWTDI!

Re: Using the 'if' statement...
by cdarke (Prior) on Jul 09, 2008 at 09:26 UTC
    The eq operator does not work like that, but you can use a regular expression:
    my $pattern = join ('|', @countries); if ($country =~ /^($pattern)$/) { print "True\n" }
    Or you could use a for loop, or map
    update: inserted parens
      Needlessly uses captures, and incorrectly assumes @countries contains regexps. Fix:
      my $pattern = join '|', map quotemeta, @countries; if ($country =~ /^(?:$pattern)$/) { print "True\n" }

      Or even

      my ($pattern) = map qr/^(?:$_)$/, join '|', map quotemeta, @countries; if ($country =~ $pattern) { print "True\n" }

      Alternatively ,below online applies
      print "True","\n" if ( grep /$country/, @countries ); # if present print "False","\n" if (! grep /$country/, @countries ); # if not prese +nt

        Needlessly uses a regexp, the regexp isn't anchored, and incorrectly assumes $country contains a regexps. Fix:

        print "True","\n" if ( grep $_ eq $country, @countries );

        I essentially posted that already.

        You need to anchor your pattern match in grep, otherwise ...
        @countries = qw(India Germany); $country = 'd'; print "True","\n" if ( grep /$country/, @countries );
Re: Using the 'if' statement...
by toolic (Bishop) on Jul 09, 2008 at 13:25 UTC
    Even more TIMTOWTDI. The core module List::Util has some handy utilities for common list operations:
    use strict; use warnings; use List::Util qw(first); my @countries = qw(India America Germany Austria); my $country = 'America'; print "True\n" if first { $_ eq $country } @countries;

    That being said, I still think the hash solutions are better.