Bare Blocks

A BLOCK by itself (labeled or not) is semantically equivalent to a loop that executes once. Thus you can use last to leave the block or redo to restart the block.[4] Note that this is not true of the blocks in eval {}, sub {}, or, much to everyone's surprise, do {}. These three are not loop blocks because they're not BLOCKs by themselves; the keyword in front makes them mere terms in an expression that just happen to include a code block. Since they're not loop blocks, they cannot be given a label to apply loop controls to. Loop controls may only be used on true loops, just as a return may only be used within a subroutine (well, or an eval).

Loop controls don't work in an if or unless, either, since those aren't loops. But you can always introduce an extra set of braces to give yourself a bare block, which does count as a loop:

if (/pattern/) {{
    last if /alpha/;
    last if /beta/;
    last if /gamma/;
    # do something here only if still in if()
}}

Here's how a block can be used to let loop-control operators work with a do{} construct. To next or redo a do, put a bare block inside:

do {{
    next if $x == $y;
    # do something here
}} until $x++ > $z;

For last, you have to be more elaborate:

{
    do {
        last if $x = $y ** 2;
        # do something here
    } while $x++ <= $z;
}

And if you want both loop controls available, you'll have put a label on those blocks so you can tell them apart:

DO_LAST: {
            do {
DO_NEXT:          {
                    next DO_NEXT if $x == $y;
                    last DO_LAST if $x =  $y ** 2;
                    # do something here
                  }
            } while $x++ <= $z;
         }

But certainly by that point (if not before), you'd be better off using an ordinary infinite loop with last at the end:

for (;;) {
    next if $x == $y;
    last if $x =  $y ** 2;
    # do something here
    last unless $x++ <= $z;
}

Case Structures

Unlike some other programming languages, Perl has no official switch or case statement. That's because Perl doesn't need one, having many ways to do the same thing. A bare block is particularly convenient for doing case structures (multiway switches). Here's one:

SWITCH: {
    if (/^abc/) { $abc = 1; last SWITCH; }
    if (/^def/) { $def = 1; last SWITCH; }
    if (/^xyz/) { $xyz = 1; last SWITCH; }
    $nothing = 1;
}

and here's another:

SWITCH: {
    /^abc/      && do { $abc = 1; last SWITCH; };
    /^def/      && do { $def = 1; last SWITCH; };
    /^xyz/      && do { $xyz = 1; last SWITCH; };
    $nothing = 1;
}

or, formatted so that each case stands out more:

SWITCH: {
    /^abc/      && do {
                        $abc = 1;
                        last SWITCH;
                   };
    /^def/      && do {
                        $def = 1;
                        last SWITCH;
                   };
    /^xyz/      && do {
                        $xyz = 1;
                        last SWITCH;
                    };
    $nothing = 1;
}

or even (horrors!):

if    (/^abc/) { $abc = 1 }
elsif (/^def/) { $def = 1 }
elsif (/^xyz/) { $xyz = 1 }
else           { $nothing = 1 }

In this next example, notice how the last operators ignore the do {} blocks, which aren't loops, and exit the for loop instead:

for ($very_nasty_long_name[$i++][$j++]->method()) {
    /this pattern/      and do { push @flags, '-e'; last; };
    /that one/          and do { push @flags, '-h'; last; };
    /something else/    and do {                    last; };
    die "unknown value: `$_'";
}

You might think it odd to loop over a single value, since you'll only go through the loop once. But it's convenient to use for/foreach's aliasing capability to make a temporary, localized assignment to $_. On repeated compares against the same long value, this makes it much easier to type and therefore harder to mistype. It avoids possible side effects from evaluating the expression again. And pertinent to this section, it's also one of the most commonly seen standard idioms for implementing a switch or case structure.

Cascading use of the ?: operator can also work for simple cases. Here we again use a for for its aliasing property to make repeated comparisons more legible:

for ($user_color_preference) {
    $value = /red/      ?   0xFF0000  :
             /green/    ?   0x00FF00  :
             /blue/     ?   0x0000FF  :
                            0x000000  ;   # black if all fail
}

For situations like this last one, it's sometimes better to build yourself a hash and quickly index into it to pull the answer out. Unlike the cascading conditionals we just looked at, a hash scales to an unlimited number of entries, and takes no more time to look up the first one than the last. The disadvantage is that you can only do an exact lookup, not a pattern match. If you have a hash like this:

%color_map = (
    azure       => 0xF0FFFF,
    chartreuse  => 0x7FFF00,
    lavender    => 0xE6E6FA,
    magenta     => 0xFF00FF,
    turquoise   => 0x40E0D0,
);

then exact string lookups run quickly:

$value = $color_map{ lc $user_color_preference } || 0x000000;

Even complicated multiway branching statements (with each case involving the execution of several different statements) can be turned into fast lookups. You just need to use a hash of references to functions. See Section 9.5 in Chapter 9, for how to handle those.



[4] For reasons that may (or may not) become clear upon reflection, a next also exits the once-through block. There is a slight difference, however: a next will execute a continue block, but a last won't.

Get Programming Perl, 3rd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.