13 Sep 2011

Perl 5.10 - Switch

 Perl 5.10 - Switch

Perl 5.10 introduces a native switch statement into the language. Like other features in Perl 5.10, we can enable it with the use feature pragma.
    use feature qw(switch);
It's also possible to enable all Perl 5.10 features with any of the following:
    use feature qw(:5.10);
    use 5.10.0;       # Requires perl v5.10.0 or later.
    use 5.010;        # Same, with nicer errors on older versions.
Perl's new switch feature is best demonstrated with an example. Let's take the classic guessing game, where the computer picks a number and our user needs to try and guess it.
    use strict;
    use warnings;
    use feature qw(switch say);
    # Pick our random number between 1 and 100
    my $secret = int(rand 100)+1;
    # An array of numbers guessed thus far
    my @guessed;
    say "Guess my number between 1-100";
    # Get each guess from the user
    while (my $guess = <STDIN>) {
        chomp $guess;
        # Check their guess using given/when
        given($guess) {
            when (/\D/)         { say "Give me an integer"; }
            when (@guessed)     { say "You've tried that";  }
            when ($secret)      { say "Just right!";  last; }
            when ($_ < $secret) { say "Too low";  continue; }
            when ($_ > $secret) { say "Too high"; continue; }
            # record the guess they've made
            push(@guessed,$_);
        }
    }
The heart of our program is the given/when block, which is enabled by the use of use feature qw(switch) at the top of our code. Let's look at each part in detail:
given($guess)
A given construct marks the start of our switch logic. It has the effect as assigning $_ to $guess for the duration of our given block. The changes to $_ are not visible outside of the given block (ie, they are lexical in scope).
when (/\D/)
when provided with a regular expression checks to see if $_ matches that regular expression. In our case, if our input contains non-digit characters, then we have something that doesn't look like an integer, and execute the associated block.
After a successful when match Perl will automatically break out of the given block unless told otherwise. In our case this repeats the loop asking the user for another guess.
when (@guessed)
In this second test we're asking whether $guess appears as a value in the @guessed. If it does we inform the user and go onto their next guess.
when ($secret)
This is a direct comparison. Is $guess the same value as $secret?. If so the player has guessed correctly! Using last allows us to break out of our while loop, ending the game.
when ($_ < $secret) and when ($_ > $secret)
These final two tests are simple comparisons. Remember that $_ contains the item we were given.
We've used continue for these statements to say that Perl should not break out of our given block on a successful match. This means that if the user guesses too low or too high, we will eventually evaluate the line:
    push(@guesses,$guess);
which remembers the guess as one we've already seen.
You can also use when with hashes (is $_ a key in the hash), subroutines (does the subroutine return true with $_ as an argument) and strings (tests for string equality). Furthermore, our given expression need not be merely a number or a string, we can also compare arrays, and hashes if we want to.
Finally, we can also set a default case, for when nothing has matched, although we have not shown it in our example.

foreach / when

If you've enabled use feature qw(switch then you can also use a foreach/when construct. Here's an example of tallying up the number of times we see cool things in a list:
    use feature qw(switch);
    foreach (@cool_things) {
        when (/pirate/)  { $pirate++ }
        when (/ninja/)   { $ninja++  }
        when (/robot/)   { $robot++  }
        say "$_ doesn't look cool...";
    }
If a when is activated, it automatically goes onto the next iteration of the foreach loop. Just like given/while, we can use the continue keyword to continue examining later options after a successful match.
It should be noted that foreach/when only works when using Perl's default $_ variable for iteration.

Lexical $_

In Perl 5.10 we can write:
    my $_;
This is allows us to do anything we like with $_, but without the possibility of changing $_ for code outside our current block. It's strongly recommended for subroutines using $_ in Perl 5.10 to avoid accidentally changing $_ for your caller.

No comments:

Post a Comment