Switch (given and when)

[ Perl tips index ]
[ Subscribe to Perl tips ]

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.

More information

For further information, we recommend the following resources:

[ Perl tips index ]
[ Subscribe to Perl tips ]


This Perl tip and associated text is copyright Perl Training Australia. You may freely distribute this text so long as it is distributed in full with this Copyright noticed attached.

If you have any questions please don't hesitate to contact us:

Email: contact@perltraining.com.au
Phone: 03 9354 6001 (Australia)
International: +61 3 9354 6001

Valid XHTML 1.0 Valid CSS