Loop Controls

10.8 Loop Controls

As you've surely noticed by now, Perl is one of the so-called "structured" programming languages. In particular, there's just 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 one 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 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. For 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. Of course, that comment line at the end is merely a comment it's not required in any way. We just threw that in to make it clearer what's happening.

There are five kinds of loop blocks in Perl. These are the blocks of for, foreach, while, until, or the naked block.[16] The curly braces of an if block or subroutine[17] don't qualify. As you may have noticed in the example above, the last operator applied to the entire loop block.

[16] Yes, you can use last to jump out of a naked block. Be sure to check your local laws before doing so.

[17] It's probably not a good 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.[18] After next, control continues with the next iteration of the loop (much like the "continue" operator in C or a similar language):

[18] 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 a little 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 $_; we'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?[19] 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 $_.

[19] 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 whether the word has any nonword characters anything but letters, digits, and underscores. So, if the word is Tom's, or 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. So, 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 already.

Like last, next may be used in any of the five kinds of loop blocks: for, foreach, while, until, or the naked block. Also, if loop blocks are nested, next works with the innermost one. We'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:[20]

[20] If you've downloaded the example files from the O'Reilly website (as described in 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 just 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 made of letters, digits, and underscores, but they can't start with a digit however, since they have no prefix character, labels could be confused with the names of builtin function names, or even with your own subroutines' names. So, it would be a poor choice to make a label called print or if. Because of that, Larry recommends that they be all uppercase. That not only ensures that the label won't conflict with another identifier but it also 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, just put the label and a colon in front of the loop. Then, 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. Notice that the label names the entire block; it's not marking a target point in the code.[21]

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

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).

It often makes sense to choose a noun as the name of the loop.[22] 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) nextWORD" or "redo (the current) LINE".

[22] 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 70's, you might not get the reference.

 



Learning Perl
Learning Perl, 5th Edition
ISBN: 0596520107
EAN: 2147483647
Year: 2001
Pages: 205

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net