Приглашаем посетить
Арцыбашев (artsybashev.lit-info.ru)

Section 10.8.  Loop Controls

Previous
Table of Contents
Next

10.8. Loop Controls

Perl is one of the "structured" programming languages. In particular, there's one entrance to any block of code, which is at the top of that block. But there are times when you may need more control or versatility than what we've shown so far. For example, you may need to make a loop like a while loop but that always runs at least once. Or maybe you need to occasionally exit a block of code early. Perl has three loop-control operators you can use in loop blocks to make the loop do all sorts of tricks.

10.8.1. The last Operator

The last operator immediately ends the execution of the loop. (If you've used the "break" operator in C or a similar language, it's like that.) It's the "emergency exit" for loop blocks. When you hit last, the loop is done as in this example:

    # Print all input lines mentioning fred, until the _ _END_ _ marker
    while (<STDIN>) {
      if (/_ _END_ _/) {
        # No more input on or after this marker line
        last;
      } elsif (/fred/) {
        print;
      }
    }
    ## last comes here ##

Once an input line has the _ _END_ _ marker, that loop is done. The comment line at the end is not required, but we threw it in to clarify what's happening.

The five kinds of loop blocks in Perl are for, foreach, while, until, and the naked block.[*] The curly braces of an if block or subroutine[Section 10.8.  Loop Controls] don't qualify. In the example above, the last operator applied to the entire loop block.

[*] Yes, you can use last to jump out of a naked block. That's not the same as jumping naked out into your block.

[Section 10.8.  Loop Controls] It's probably a bad idea, but you could use these loop control operators from inside a subroutine to control a loop that is outside the subroutine. That is, if a subroutine is called in a loop block and the subroutine executes last when there's no loop block running inside the subroutine, the flow of control will jump to just after the loop block in the main code. This ability to use loop control from within a subroutine may go away in a future version of Perl, and no one is likely to miss it.

The last operator will apply to the innermost currently running loop block. To jump out of outer blocks, stay tuned; that's coming up in a little bit.

10.8.2. The next Operator

Sometimes you're not ready for the loop to finish, but you're done with the current iteration. That's what the next operator is good for. It jumps to the inside of the bottom of the current loop block.[Section 10.8.  Loop Controls] After next, control continues with the next iteration of the loop (much like the "continue" operator in C or a similar language):

[Section 10.8.  Loop Controls] This is another of our many lies. In truth, next jumps to the start of the (usually omitted) continue block for the loop. See the perlsyn manpage for the full details.

    # Analyze words in the input file or files
    while (<>) {
      foreach (split) {  # break $_ into words, assign each to $_ in turn
        $total++;
        next if /\W/;    # strange words skip the remainder of the loop
        $valid++;
        $count{$_}++;    # count each separate word
        ## next comes here ##
      }
    }

    print "total things = $total, valid words = $valid\n";
    foreach $word (sort keys %count) {
      print "$word was seen $count{$word} times.\n";
    }

This one is more complex than most of our examples up to this point, so let's take it step by step. The while loop is reading lines of input from the diamond operator, one after another, into $_; you've seen that before. Each time through that loop, another line of input will be in $_.

Inside that loop, the foreach loop is iterating over the return value split. Do you remember the default for split with no arguments?[*] That splits $_ on whitespace, in effect breaking $_ into a list of words. Since the foreach loop doesn't mention some other control variable, the control variable will be $_. So, we'll see one word after another in $_.

[*] If you don't remember it, don't worry too much. Don't waste any brain cells remembering things that you can look up with perldoc.

But didn't we just say that $_ holds one line of input after another? Well, in the outer loop, that's what it is. But inside the foreach loop, it holds one word after another. It's no problem for Perl to reuse $_ for a new purpose; this happens all the time.

Now, inside the foreach loop, we're seeing one word at a time in $_. $total is incremented, so it must be the total number of words. But the next line (which is the point of this example) checks to see if the word has any nonword characters: anything but letters, digits, and underscores. So, if the word is Tom's, if it is full-sized, or if it has an adjoining comma, quote mark, or any other strange character, it will match that pattern and we'll skip the rest of the loop, going on to the next word.

But let's say that it's an ordinary word, like fred. In that case, we count $valid up by one and also $count{$_}, keeping a count for each different word. When we finish the two loops, we've counted every word in every line of input from every file the user wanted us to use.

We're not going to explain the last few lines. By now, we hope you've got stuff like that down.

Like last, next may be used in any of the five kinds of loop blocks: for, foreach, while, until, or the naked block. If loop blocks are nested, next works with the innermost one. You'll see how to change that at the end of this section.

10.8.3. The redo Operator

The third member of the loop control triad is redo. It says to go back to the top of the current loop block, without testing any conditional expression or advancing to the next iteration. (If you've used C or a similar language, you've never seen this one before. Those languages don't have this kind of operator.) Here's an example:

    # Typing test
    my @words = qw{ fred barney pebbles dino wilma betty };
    my $errors = 0;

    foreach (@words) {
      ## redo comes here ##
      print "Type the word '$_': ";
      chomp(my $try = <STDIN>);
      if ($try ne $_) {
        print "Sorry - That's not right.\n\n";
        $errors++;
        redo;  # jump back up to the top of the loop
      }
    }
    print "You've completed the test, with $errors errors.\n";

Like the other two operators, redo will work with any of the five kinds of loop blocks, and it will work with the innermost loop block when they're nested.

The big difference between next and redo is that next will advance to the next iteration, but redo will redo the current iteration. Here's an example program that you can play with to get a feel for how these three operators work:[*]

[*] If you've downloaded the example files from the O'Reilly web site (see the Preface), you'll find this program called lnr-example.

    foreach (1..10) {
      print "Iteration number $_.\n\n";
      print "Please choose: last, next, redo, or none of the above? ";
      chomp(my $choice = <STDIN>);
      print "\n";
      last if $choice =~ /last/i;
      next if $choice =~ /next/i;
      redo if $choice =~ /redo/i;
      print "That wasn't any of the choices... onward!\n\n";
    }
    print "That's all, folks!\n";

If you press return without typing anything (try it two or three times), the loop counts along from one number to the next. If you choose last when you get to number four, the loop is done and you won't go on to number five. If you choose next when you're on four, you're on to number five without printing the "onward" message. And if you choose redo when you're on four, you're back to doing number four all over again.

10.8.4. Labeled Blocks

When you need to work with a loop block that's not the innermost one, use a label. Labels in Perl are like other identifiers; they are made of letters, digits, and underscores, but they can't start with a digit. Since they have no prefix character, labels could be confused with the names of built-in function names or with your own subroutines' names. It would be a poor choice to make a label called print or if. Because of that, Larry recommends they be all uppercase. That ensures the label won't conflict with another identifier and makes it easy to spot the label in the code. In any case, labels are rare, only showing up in a small percentage of Perl programs.

To label a loop block, put the label and a colon in front of the loop. Inside the loop, you may use the label after last, next, or redo as needed:

    LINE: while (<>) {
      foreach (split) {
        last LINE if /_ _END_ _/;  # bail out of the LINE loop
        ...
      }
    }

For readability, it's generally nice to put the label at the left margin even if the current code is at a higher indentation. The label names the entire block; it's not marking a target point in the code.[*]In that previous snippet of sample code, the special _ _END_ _ token marks the end of all input. Once that token shows up, the program will ignore any remaining lines (even from other files).

[*] This isn't goto, after all.

It often makes sense to choose a noun as the name of the loop.[Section 10.8.  Loop Controls] That is, the outer loop is processing a line at a time, so we called it LINE. If we had to name the inner loop, we would have called it WORD since it processes a word at a time. That makes it convenient to say things like "(move on to the) next WORD" or "redo (the current) LINE."

[Section 10.8.  Loop Controls] That is, it makes more sense to do that than not to do that. Perl doesn't care if you call your loop labels things like XYZZY or PLUGH. However, unless you were friendly with the Colossal Cave in the `70s, you might not get the reference.

    Previous
    Table of Contents
    Next