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


Hello All,
I have a problem with a regular expression.
What I want it to do is action some code if a looping variable is equal to certain values
I have tried  $i  =~ /[0,5,8,15,20]/) but $i gets processed when 10, 11, 12 etc....
Obviously I havent quite got this right
Suggestions / Answers on a plate please???????

Kev

Edit Masem 2001-08-31 - Fixed a code tag

Replies are listed 'Best First'.
Re: Reg Expression Question
by blakem (Monsignor) on Aug 31, 2001 at 12:00 UTC
    I think you really want a hash...
    my %hash = map {$_=>1} (0,5,8,15,20); for my $i (0..30) { print "I am number $i\n" if $hash{$i}; }
    Or, less idiomatically

    my %hash = (0 => 1, 5 => 1, 8 => 1, 15 => 1, 20 => 1); for my $i (0..30) { if ($hash{$i}) { print "I am number $i\n"; } }

    -Blake

Re: Reg Expression Question
by bwana147 (Pilgrim) on Aug 31, 2001 at 12:08 UTC

    You should use grep for that:

    if ( grep { $_ == $i } (0, 5, 8, 15, 20) ) { ... }

    And read perlre to learn what regexes are and what they are not.

    --bwana147

      Thanks for the answer

      I do understand basic reg expressions, but I was unsure how to use the range on numbers...

      But either answer will do me!!!
      Kev
Re: Reg Expression Question
by clemburg (Curate) on Aug 31, 2001 at 12:02 UTC

    H:\>perldoc -q switch > switch.txt H:\>cat switch.txt Found in d:\Perl\activeperl522\lib\pod\perlfaq7.pod How do I create a switch or case statement? This is explained in more depth in the the perlsyn manpage. Briefly, there's no official case statement, because of the variety of tests possible in Perl (numeric comparison, string comparison, glob comparison, regexp matching, overloaded comparisons, ...). Larry couldn't decide how best to do this, so he left it out, even though it's been on the wish list since perl1. The general answer is to write a construct like this: for ($variable_to_test) { if (/pat1/) { } # do something elsif (/pat2/) { } # do something else elsif (/pat3/) { } # do something else else { } # default } Here's a simple example of a switch based on pattern matching, this time lined up in a way to make it look more like a switch statement. We'll do a multi-way conditional based on the type of reference stored in $whatchamacallit: SWITCH: for (ref $whatchamacallit) { /^$/ && die "not a reference"; /SCALAR/ && do { print_scalar($$ref); last SWITCH; }; /ARRAY/ && do { print_array(@$ref); last SWITCH; }; /HASH/ && do { print_hash(%$ref); last SWITCH; }; /CODE/ && do { warn "can't print function + ref"; last SWITCH; }; # DEFAULT warn "User defined type skipped"; } See `perlsyn/"Basic BLOCKs and Switch Statements"' for many other examples in this style. Sometimes you should change the positions of the constant and the variable. For example, let's say you wanted to test which of many answers you were given, but in a case-insensitive way that also allows abbreviations. You can use the following technique if the strings all start with different characters, or if you want to arrange the matches so that one takes precedence over another, as `"SEND"' has precedence over `"STOP"' here: chomp($answer = <>); if ("SEND" =~ /^\Q$answer/i) { print "Action is se +nd\n" } elsif ("STOP" =~ /^\Q$answer/i) { print "Action is st +op\n" } elsif ("ABORT" =~ /^\Q$answer/i) { print "Action is ab +ort\n" } elsif ("LIST" =~ /^\Q$answer/i) { print "Action is li +st\n" } elsif ("EDIT" =~ /^\Q$answer/i) { print "Action is ed +it\n" } A totally different approach is to create a hash of function references. my %commands = ( "happy" => \&joy, "sad", => \&sullen, "done" => sub { die "See ya!" }, "mad" => \&angry, ); print "How are you? "; chomp($string = <STDIN>); if ($commands{$string}) { $commands{$string}->(); } else { print "No such command: $string\n"; }

    Christian Lemburg
    Brainbench MVP for Perl
    http://www.brainbench.com

      I know how to create a switch statement, but I wanted a quick one liner.
      I have tried BlakeM's solution and it works admirably
      so I'll go with that thanks.
Re: Reg Expression Question
by azatoth (Curate) on Aug 31, 2001 at 12:00 UTC
      $i =~ /[0|5|8|15|20]/

      But that will also match 30,15,888,150,200, etc...

      If you really want to use a regex for this, you'll need to anchor it like so:

      $i =~ /^(0|5|8|15|20)$/;
      Though I still think a hash is a better solution.

      Update: changed the square brackets to parens, thanks Hofmator...

      -Blake

      Well, no ... $i =~ /[0|5|8|15|20]/;matches one character which is either 0, 1, 2, 5, 8 or |. And only if you correct the typo ;-)

      What you can do is use parenthesis like this $i =~ /^(0|5|8|15|20)$/; but make sure you have the start and end of string assertions, otherwise you will match much more numbers.

      Apart from that I think a hash would be a better solution as already mentioned elsewhere.

      -- Hofmator

      That would also match 1 which is incorrect.
      $i = 1; print "found\n" if $i =~ /[0|5|8|15|20]/;
      Sorry, Cut & pasted code.
      Original Code has square brackets and it doesnt work!!
      Will try the hash idea?
      Thanks All
Re: Reg Expression Question
by pix (Novice) on Aug 31, 2001 at 13:44 UTC
    you could do something like that:

    $okvalues = "0 20 34 23 5"; for($i = 1; $i < 100; $i++) { if($okvalues =~ m/$i/) { print "$i\n"; } }

    Federico

      Did you try the code you suggested above? For output I get:

      2 3 4 5 20 23 34
      It incorrectly matches on 2,3 and 4. Now if you had modified your regex to be:
      $okvalues =~ m/\b$i\b/
      You wouldn't have that problem, because the \b assertion is only true between a word char and a non wordchar. This stops '3' from matching on '34'. Though, its still not a good idea to do it this way.

      -Blake