10.8. Loop ControlsAs 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 OperatorThe 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.[229] The curly braces of an if block or subroutine[230] don't qualify. As you may have noticed in the example above, the last operator applied to the entire loop block.
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 OperatorSometimes 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.[231] After next, control continues with the next iteration of the loop (much like the "continue" operator in C or a similar language):
# 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?[232] 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 $_.
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 OperatorThe 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:[233]
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 BlocksWhen 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.[234]
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.[235] 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".
Copyright © 2002 O'Reilly & Associates. All rights reserved. |
|