Chapter 8
More Control Structures
CONTENTS
On Chapter 2, "Basic Operators and Control Flow," you learned
about some of the simpler conditional statements in Perl, including
the following:
- The if statement, which defines statements that are
executed only when a certain condition is true
- The if-else statement, which chooses between
two alternatives
- The if-elsif-else statement, which
chooses between multiple alternatives
- The unless statement, which defines statements that
are executed unless a specified condition is true
- The while statement, which executes a group of statements
while a specified condition is true
- The until statement, which executes a group of statements
until a specified condition is true
ToChapter's lesson talks about the other control structures in Perl;
these control structures give you a great deal of flexibility
when you are determining the order of execution of your program
statement.
ToChapter you learn the following control structures:
- Single-line conditional statements
- The for statement
- The foreach statement
- The do statement
- The last statement
- The next statement
- The redo statement
- The continue statement
- Labeled blocks
- The goto statement
On Chapter 2 you saw the if statement, which works as follows:
if ($var == 0) {
print ("This is zero.\n");
}
If the statement block inside the if statement consists
of only one statement, Perl enables you to write this in a more
convenient way using a single-line conditional statement.
This is a conditional statement whose statement block contains
only one line of code.
The following single-line conditional statement is identical to
the if statement defined previously:
print ("This is zero.\n") if ($var == 0);
Single-line conditional statements also work with unless,
while, and until:
print ("This is zero.\n") unless ($var != 0);
print ("Not zero yet.\n") while ($var-- > 0);
print ("Not zero yet.\n") until ($var-- == 0);
In all four cases, the syntax of the single-line conditional statement
is the same.
The syntax for the single-line conditional statement is
statement keyword condexpr
Here, statement is any Perl statement. keyword
is either if, unless, while, or until.
condexpr is the conditional expression that is evaluated.
statement is executed in the following cases:
- If keyword is if, statement is executed
if condexpr is true.
- If keyword is unless, statement is executed
unless condexpr is true.
- If keyword is while, statement is executed
while condexpr is true.
- If keyword is until, statement is executed
until condexpr is true.
To see how single-line conditional expressions can be useful,
look at the following examples, starting with Listing 8.1. This
is a simple program that copies one file to another. Single-line
conditional statements are used to check whether the files opened
successfully, and another single-line conditional statement actually
copies the file.
Listing 8.1. A program that uses single-line conditional statements
to copy one file to another.
1: #!/usr/local/bin/perl
2:
3: die ("Can't open input\n") unless (open(INFILE, "infile"));
4: die ("Can't open output\n") unless (open(OUTFILE, ">outfile"));
5: print OUTFILE ($line) while ($line = <INFILE>);
6: close (INFILE);
7: close (OUTFILE);
There is no output; this program writes to a file.

As you can see, this program is clear and concise.
Instead of using three lines to open a file and check it, as in
unless (open (INFILE, "infile")) {
die ("Can't open input\n");
}
you can now use just one:
die ("Can't open input\n") unless (open(INFILE, "infile"));
Line 3 opens the input file. If the open is not successful, the
program terminates by calling die.
Line 4 is similar to line 3. It opens the output file and checks
whether the file actually is open; if the file is not open, the
program terminates.
Line 5 actually copies the file. The conditional expression
$line = <INFILE>
reads a line from the file represented by the file variable INFILE
and assigns it to $line. If the line is empty, the conditional
expression is false, and the while statement stops executing.
If the line is not empty, it is written to OUTFILE.
| NOTE |
The conditional expression in a single-line conditional statement is always executed first, even though it appears at the end of the statement. For example:
print OUTFILE ($line) while ($line = <INFILE>);
Here, the conditional expression that reads a line of input and assigns it to $line is always executed first. This means that print is not called until $line contains something to print. This also means that the call to
print is never executed if INFILE is an empty file (which is what you want).
Because single-line conditional expressions are "backward," be careful when you use them with anything more complicated than what you see here.
|
You can use the single-line conditional statement in conjunction
with the autoincrement operator ++ to write a loop in a single
line. For example, examine Listing 8.2, which prints the numbers
from 1 to 5 using a single-line conditional statement.
Listing 8.2. A program that loops using a single-line conditional
statement.
1: #!/usr/local/bin/perl
2:
3: $count = 0;
4: print ("$count\n") while ($count++ < 5);
$ program8_2
1
2
3
4
5
$

When the Perl interpreter executes line 3,
it first evaluates the conditional expression
$count++ < 5
Because the ++ appears after $count, 1 is added
to the value of $count after the conditional expression
is evaluated. This means that $count has the value
0, not 1, the first time the expression is evaluated. Similarly,
$count has the value 1 the second time, 2 the
third time, 3 the fourth time, and 4 the fifth time. In each of
these five cases, the conditional expression evaluates to true,
which means that the loop iterates five times.
After the conditional expression has been evaluated, the ++
operator adds 1 to the value of $count. This new value
of $count is then printed. This means that when the loop
is first executed, the call to print prints 1, even though
the value of $count was 0 when the conditional expression
was evaluated.
Although single-line conditional statements that contain loops
are useful, there are problems. Consider Listing 8.2, which you've
just seen. It is easy to forget that $count has to be
initialized to one less than the first value you want to use in
the loop, and that the conditional expression has to use the <
operator, not the <= operator.
For example, take a look at the following:
$count = 1;
print ("$count\n") while ($count++ < 5);
Here, you have to look closely to see that the first value printed
is 2, not 1.
Here is another loop containing a mistake:
$count = 0;
print ("$count\n") while ($count++ <= 5);
This loop iterates six times, not five; the sixth time through
the loop, $count has the value 5 when the conditional
expression is evaluated. The expression evaluates to true, $count
is incremented to 6, and print therefore prints the value
6.
Here is a related but slightly more subtle problem:
$count = 0;
print ("$count\n") while ($count++ < 5);
print ("The total number of iterations is $count.\n");
This loop iterates five times, which is what you want. However,
after the conditional expression is evaluated for the final time,
the value of $count becomes 6, as follows:
- Before the conditional expression is evaluated, $count
has the value 5.
- Because the value of $count is not less than 5, the
conditional expression evaluates to false, which terminates the
loop.
- After the conditional expression is evaluated, the ++
operator adds one to $count, giving it the value 6.
This means that the final print statement prints the
following, which is probably not what you want:
The total number of iterations is 6.
 |
DO use the for statement as a convenient way to write a concise, compact loop. It is discussed in the next section.
DON'T use the ++ operator to produce a loop in a single-line conditional statement unless it's absolutely necessary. It's just too easy to go wrong with it.
|
Many of the programs that you've seen so far use the while
statement to create a program loop. Here is a simple example:
$count = 1;
while ($count <= 5) {
# statements inside the loop go here
$count++;
}
This loop contains three items that control it:
- A statement that sets the initial value of the loop. In this
loop, the scalar variable $count is used to control the
number of iterations of the loop, and the statement
$count = 1;
sets the initial value of $count to 1. Statements
such as this are called loop initializers.
- A conditional expression that checks to see whether to continue
iterating the loop. In this case, the conditional expression
$count <= 5
is evaluated; if it is false, the loop is terminated.
- A statement that changes the value of the variable which is
tested in the conditional expression. In this loop, the statement
count++;
adds 1 to the value of $count, which is the scalar
variable being tested in the conditional expression. Statements
such as this are called loop iterators.
Perl enables you to put the three components that control a loop
together on a single line using a for statement. For
example, the following statement is equivalent to the loop you've
been looking at:
for ($count=1; $count <= 5; $count++) {
# statements inside the loop go here
}
Here, the three controlling components-the loop initializer, the
conditional expression, and the loop iterator-appear together,
and are separated by semicolons.
The syntax of the for statement is
for (expr1; expr2; expr3) {
statement_block
}
expr1 is the loop initializer. It is evaluated only once,
before the start of the loop.
expr2 is the conditional expression that terminates the
loop. The conditional expression in expr2 behaves just
like the ones in while and if statements. If
its value is 0 (false), the loop is terminated, and if its value
is nonzero, the loop is executed.
statement_block is the collection of statements that
is executed if (and when) expr2 has a nonzero value.
expr3 is executed once per iteration of the loop and
is executed after the last statement in statement_block
is executed.
| NOTE |
If you know the C programming language, the for statement will be familiar to you. The for statement in Perl is syntactically identical to the for statement in C.
|
Listing 8.3 is a program based on the example for statement
you've just seen.
Listing 8.3. A program that prints the numbers from 1 to 5
using the for
statement.
1: #!/usr/local/bin/perl
2:
3: for ($count=1; $count <= 5; $count++) {
4: print ("$count\n");
5: }
$ program8_3
1
2
3
4
5
$

Line 3 of the program is the start of the for
statement. The first expression defined in the for statement,
$count = 1, is the loop initializer; it is executed before
the loop is iterated.
The second expression defined in the for statement, $count
<= 5, tests whether to continue iterating the loop.
The third expression defined in the for statement, $count++,
is evaluated after the last statement in the loop, line 4, is
executed.
As you can see from the output, the loop is iterated five times.
| TIP |
Use the for statement instead of while or until whenever possible; when you use the for statement, it is easier to avoid infinite loops.
For example, when you use a while statement, it's easy to forget to iterate the loop. The following is an example:
$count = 1;
while ($count <= 5) {
print ("$count\n");
}
The equivalent statement using for is
for ($count = 1; $count <= 5; ) {
print ("$count\n");
}
When you use the for statement, it is easier to notice that the loop iterator is missing.
|
Some loops need to perform more than one action before iterating.
For example, consider the following loop, which reads four lines
of input from the standard input file and prints three of them:
$line = <STDIN>;
$count = 1;
while ($count <= 3) {
print ($line);
$line = <STDIN>;
$count++;
}
This loop needs two loop initializers and two loop iterators:
one of each for the variable $count, and one of each
to read another line of input from STDIN.
At first glance, you might think that you can't write this loop
using the for statement. However, you can use the comma
operator to combine the two loop initializers and the two loop
iterators into single expressions. Listing 8.4 does this.
Listing 8.4. A program that uses the for
statement to read four input lines and write three of them.
1: #!/usr/local/bin/perl
2:
3: for ($line = <STDIN>, $count = 1; $count <= 3;
4: $line = <STDIN>, $count++) {
5: print ($line);
6: }
$ program8_4
This is my first line.
This is my first line.
This is my second line.
This is my second line.
This is my last line.
This is my last line.
This input line is not written out.
$

The loop initializer in this for statement
is the expression
$line = <STDIN>, $count = 1
The comma operator in this expression tells the Perl interpreter
to evaluate the first half of the expression-the part to the left
of the comma-and then evaluate the second half. The first half
of this expression reads a line from the standard input file and
assigns it to $line; the second half of the expression
assigns 1 to $count.
The loop iterator also consists of two parts:
$line = <STDIN>, $count++
This expression reads a line from the standard input file and
adds 1 to the variable keeping track of when to terminate the
loop, which is $count.
 |
Don't use the for statement if you have a large number of loop initializers or loop iterators, because statements that contain a large number of comma operators are difficult to read.
|
One common use of loops is to perform an operation on every element
of a list stored in an array variable. For example, the following
loop checks whether any element of the list stored in the array
variable @words is the word the:
$count = 1;
while ($count <= @words) {
if ($words[$count-1] eq "the") {
print ("found the word 'the'\n");
}
$count++;
}
As you've seen, you can use the for statement to simplify
this loop, as follows:
for ($count = 1; $count <= @words; $count++) {
if ($words[$count-1] eq "the") {
print ("found the word 'the'\n");
}
}
Perl provides an even simpler way to do the same thing, using
the foreach statement. The following loop, which uses
foreach, is identical to the preceding one:
foreach $word (@words) {
if ($word eq "the") {
print ("found the word 'the'\n");
}
}
The syntax for the foreach statement is
foreach localvar (listexpr) {
statement_block;
}
Here, listexpr is any list or array variable, and statement_block
is a collection of statements that is executed every time the
loop iterates.
localvar is a scalar variable that is defined only for
the duration of the foreach statement. The first time
the loop is executed, localvar is assigned the value
of the first element of the list in listexpr. Each subsequent
time the loop is executed, localvar is assigned the value
of the next element of listexpr.
Listing 8.5 shows how this works.
Listing 8.5. A demonstration of the foreach
statement.
1: #!/usr/local/bin/perl
2:
3: @words = ("Here", "is", "a", "list.");
4: foreach $word (@words) {
5: print ("$word\n");
6: }
$ program8_5
Here
is
a
list.
$

The foreach statement in line 4 assigns
a word from @list to the local variable $word.
The first time the loop is executed, the value stored in $word
is the string Here. The second time the loop is executed,
the value stored in $word is is. Subsequent
iterations assign a and list. to $word.
The loop defined by the foreach statement terminates
after all of the words in the list have been assigned to $word.
| NOTE |
In Perl, the for statement and the foreach statement are actually synonymous: you can use for wherever foreach is expected, and vice versa.
|
Note that the scalar variable defined in the foreach
statement is defined only for the duration of the loop. If a value
is assigned to the scalar variable prior to the execution of the
foreach statement, this value is restored after the foreach
is executed. Listing 8.6 shows how this works.
Listing 8.6. A program that uses the same name inside and outside
a foreach
statement.
1: #!/usr/local/bin/perl
2:
3: $temp = 1;
4: @list = ("This", "is", "a", "list", "of", "words");
5: print ("Here are the words in the list: \n");
6: foreach $temp (@list) {
7: print ("$temp ");
8: }
9: print("\n");
10: print("The value of temp is now $temp\n");
$ program8_6
Here are the words in the list:
This is a list of words
The value of temp is now 1
$

Line 3 assigns 1 to the scalar variable $temp.
The foreach statement that prints the words in the list
is defined in lines 6-8. This statement assigns the elements of
@list to $temp, one per iteration of the loop.
After the loop is terminated, the original value of $temp
is restored, which is 1. This value is printed by line 10.
Variables (such as $temp in lines 6-8) that are only
defined for part of a program are known as local variables;
variables that are defined throughout a program are known as global
variables. You'll see more examples of local variables on
Chapter 9, "Using Subroutines."
| TIP |
It is not a good idea to use $temp the way it is used in Listing 8.6, namely, as both a local and a global variable. You might forget that the value of the global variable-in the case of $temp, the value 1-is overwritten by the value
assigned in the foreach statement.
Conversely, you might forget that the value assigned to $temp in the foreach statement is lost when the foreach is finished.
It is better to define a new scalar variable name for the local variable, to avoid confusion.
|
Note that changing the value of the local variable inside a foreach
statement also changes the value of the corresponding element
of the list. For example:
@list = (1, 2, 3, 4, 5);
foreach $temp (@list) {
if ($temp == 2) {
$temp = 20;
}
}
In this loop, when $temp is equal to 2, $temp
is reset to 20. Therefore, the list stored in the array
variable @list becomes (1, 20, 3, 4, 5).
Use this feature with caution, because it is not obvious that
the value of @list has changed.
So far, all of the examples of the foreach statement
that you've seen have iterated using the contents of an array
variable. For example, consider the following:
@list = ("This", "is", "a", "list");
foreach $temp (@list) {
print ("$temp ");
}
This loop assigns This to $temp the first time
through the loop, and then assigns is, a, and
list to $temp on subsequent iterations.
You also can use list constants or the return values from functions
in foreach statements. For example, the preceding statements
can be written as follows:
foreach $temp ("This", "is", "a", "list") {
print("$temp ");
}
As before, $temp is assigned This, is,
a, and list in successive iterations of the
foreach loop.
Listing 8.7 shows how you can use the return value from a function
as a loop iterator.
Listing 8.7. A program that prints out the words in a line
in reverse-sorted order.
1: #!/usr/local/bin/perl
2:
3: $line = <STDIN>;
4: $line =~ s/^\s+//;
5: $line =~ s/\s+$//;
6: foreach $word (reverse sort split(/[\t ]+/, $line)) {
7: print ("$word ");
8: }
9: print ("\n");
$ program8_7
here is my test line
test my line is here
$

Before splitting the input line into words
using split, this program first removes the leading and
trailing white space. (If leading and trailing space is not removed,
split creates an empty word.) Line 4 removes leading
spaces and tabs from the input line. Line 5 removes any trailing
spaces and tabs as well as the closing newline character.
Lines 6-8 contain the foreach loop. The list used in
this loop is created as follows:
- First, split breaks the input line into words. The
list returned by split is ("here", "is",
"my", "test", "line").
- The list returned by split is passed to the built-in
function sort, which sorts the list. The list returned
by sort is ("here", "is", "line",
"my", "test").
- The list returned by sort is passed to another built-in
function, reverse. This reverses the sorted list, producing
the list ("test", "my", "line",
"is", "here").
- Each element of the list returned by reverse is assigned,
in turn, to the local scalar variable $word, starting
with "test" and proceeding from there.
Line 7 prints the current value stored in $word. Each
time the foreach loop iterates, a different value in
the list is printed.
| NOTE |
The code fragment
foreach $word (reverse sort split(/[\t ]+/, $line))
shows why omitting parentheses when calling built-in functions can sometimes be useful. If all the parentheses are included, this becomes
foreach $word (reverse(sort(split(/[\t ]+/, $line))))
which is not as readable.
|
So far, all of the loops you've seen test the conditional expression
before executing the loop. Perl enables you to write loops that
always execute at least once using the do statement.
The syntax for the do statement is
do {
statement_block
} while_or_until (condexpr);
As in other conditional statements, such as the if statement
and the while statement, statement_block is
a block of statements to be executed, and condexpr is
a conditional expression.
while_or_until is either the while keyword or
the until keyword. If you use while, statement_block
loops while condexpr is true. For example:
do {
$line = <STDIN>;
} while ($line ne "");
This loops while $line is non-empty (in other words,
while the program has not reached the end of file).
If you use until, statement_block loops until
condexpr is true. For example:
do {
$line = <STDIN>;
} until ($line eq "");
This reads from the standard input file until $line is
empty (again, until end of file is reached).
Listing 8.8 is a simple example of a program that uses a do
statement.
Listing 8.8. A simple example of a do
statement.
1: #!/usr/local/bin/perl
2:
3: $count = 1;
4: do {
5: print ("$count\n");
6: $count++;
7: } until ($count > 5);
$ program8_8
1
2
3
4
5
$

Lines 4-7 contain the do statement,
which loops five times. Line 7 tests whether the counting variable
$count is greater than 5.
| NOTE |
The do statement can also be used to call subroutines. See Chapter 9, "Using Subroutines," for more information.
|
Normally, you exit a loop by testing the conditional expression
that is part of the loop. For example, if a loop is defined by
the while statement, as in the following, the program
exits the loop when the conditional expression at the top of the
loop, $count <= 10, is false:
while ($count <= 10) {
# statements go here
}
In the preceding case, the program can exit the loop only after
executing all of the statements in it. Perl enables you to define
an exit point anywhere in the loop using a special last
statement.
The syntax for the last statement is simple:
last;
To see how the last statement works, take a look at Listing
8.9, which adds a list of numbers supplied by means of the standard
input file.
Listing 8.9. A program that exits using the last
statement.
1: #!/usr/local/bin/perl
2:
3: $total = 0;
4: while (1) {
5: $line = <STDIN>;
6: if ($line eq "") {
7: last;
8: }
9: chop ($line);
10: @numbers = split (/[\t ]+/, $line);
11: foreach $number (@numbers) {
12: if ($number =~ /[^0-9]/) {
13: print STDERR ("$number is not a number\n");
14: }
15: $total += $number;
16: }
17: }
18: print ("The total is $total.\n");
$ program8_9
4 5 7
2 11 6
^D
The total is 35.
$

The loop that reads and adds numbers starts
on line 4. The conditional expression at the top of this loop
is the number 1. Because this is a nonzero number, this conditional
expression always evaluates to true. Normally, this means that
the while statement loops forever; however, because this
program contains a last statement, the loop eventually
terminates.
Line 6 checks whether the program has reached the end of the standard
input file. To do this, it checks whether the line read from the
standard input file, now stored in $line, is empty. (Recall
that the Ctrl+D character, written here as ^D, marks
the standard input file as empty.)
If the line is empty, line 7, the last statement, is
executed. This statement tells the Perl interpreter to terminate
executing the loop and to continue with the first statement after
the loop, which is line 18.
Lines 10-16 add the numbers on the input line to the total stored
in the scalar variable $total. Line 10 breaks the line
into individual numbers, and lines 11-16 add each number, in turn,
to $total.
Line 12 checks whether each number actually consists of the digits
0-9. The pattern [^0-9] matches anything that is not
a digit; if the program finds such a character, it flags the number
as erroneous. (The program can produce empty words if leading
or trailing spaces or tabs exist in the line; this is not a problem,
because [^0-9] doesn't match an empty word.)
| NOTE |
You can use the last statement with a single-line conditional statement. For example,
last if ($count == 5);
terminates the loop if the value of $count is 5.
|
 |
You cannot use the last statement inside the do statement. Although the do statement behaves like the other control structures, it is actually implemented differently.
|
In Perl, the last statement terminates the execution
of a loop. To terminate a particular iteration of a loop, use
the next statement.
Like last, the syntax for the next statement
is simple:
next;
Listing 8.10 is an example that uses the next statement.
It sums up the numbers from 1 to a user-specified upper limit
and also produces a separate sum of the numbers divisible by 2.
Listing 8.10. A program that sums the numbers from 1 to a specified
number and also sums the even numbers.
1: #!/usr/local/bin/perl
2:
3: print ("Enter the last number in the sum:\n");
4: $limit = <STDIN>;
5: chop ($limit);
6: $count = 1;
7: $total = $eventotal = 0;
8: for ($count = 1; $count <= $limit; $count++) {
9: $total += $count;
10: if ($count % 2 == 1) {
11: # start the next iteration if the number is odd
12: next;
13: }
14: $eventotal += $count;
15: }
16: print("The sum of the numbers 1 to $limit is $total\n");
17: print("The sum of the even numbers is $eventotal\n");
$ program8_10
Enter the last number in the sum:
7
The sum of the numbers 1 to 7 is 28
The sum of the even numbers is 12
$

The loop in lines 8-15 adds the numbers together.
The start of the for statement in line 8 loops five times;
the counter variable, $count, is assigned the values
1, 2, 3, 4, and 5 in successive iterations.
Line 9 adds to the total of all the numbers. This statement is
always executed.
Line 10 tests whether the current number-the current value of
$count-is even or odd. If $count is even, the
conditional expression
$count % 2 == 1
is false, and program execution continues with line 14. If the
current value of $count is odd, the Perl interpreter
executes line 12, the next statement. This statement
tells the Perl in-terpreter to start the next iteration of the
loop.
Note that the loop iterator in the for statement, $count++,
is still executed, even though the next statement skips
over part of the loop. This ensures that the program does not
go into an infinite loop.
Because the next statement is executed when the value
of $count is odd, line 14 is skipped in this case. This
means that the value of $count is added only when it
is even.
 |
Be careful when you use next in a while or until loop. The following example goes into an infinite loop:
$count = 0;
while ($count <= 10) {
if ($count == 5) {
next;
}
$count++;
}
When $count is 5, the program tells Perl to start the next iteration of the loop. However, the value of $count is not changed, which means that the expression $count == 5 is still true.
To get rid of this problem, you need to increment $count before using next, as in the following:
$count = 0;
while ($count <= 10) {
if ($count == 5) {
$count++;
next;
}
$count++;
}
This, by the way, is why many programming purists dislike statements such as next and last-it's too easy to lose track of where you are and what needs to be updated.
|
The next statement enables you to check for and ignore
unusual conditions when reading input. For example, Listing 8.11
counts the number of words in the input read from the standard
input file. It uses the next statement to skip blank
lines.
Listing 8.11. A word-counting program that uses the next
statement.
1: #!/usr/local/bin/perl
2:
3: $total = 0;
4: while ($line = <STDIN>) {
5: $line =~ s/^[\t ]*//;
6: $line =~ s/[\t ]*\n$//;
7: next if ($line eq "");
8: @words = split(/[\t ]+/, $line);
9: $total += @words;
10: }
11: print ("The total number of words is $total\n");
$ program8_11
Here is my test input.
It contains some words.
^D
The total number of words is 9
$

After line 4 has read a line of input and checked
that it is not empty (which means that the end of file has not
been reached), the program then gets rid of leading spaces and
tabs (line 5) and trailing spaces, tabs, and the trailing newline
(line 6). If a line is blank, lines 5 and 6 turn it into the empty
string, for which line 7 tests.
Line 7 contains the next statement as part of a single-line
conditional statement. If the line is now empty, the next
statement tells the program to go to the beginning of the loop
and read in the next line of input.
 |
You cannot use the next statement inside the do statement. Although the do statement behaves like the other control structures, it is actually implemented differently.
|
Perl enables you to tell the Perl interpreter to restart an iteration
of a loop using the redo statement.
Like last and next, the syntax for the redo
statement is simple:
redo;
For an example, look at Listing 8.12, which counts the number
of words in three non-blank input lines.
Listing 8.12. A word-counting program that uses the redo
statement.
1: #!/usr/local/bin/perl
2:
3: $total = 0;
4: for ($count = 1; $count <= 3; $count++) {
5: $line = <STDIN>;
6: last if ($line eq "");
7: $line =~ s/^[\t ]*//;
8: $line =~ s/[\t ]*\n$//;
9: redo if ($line eq "");
10: @words = split(/[\t ]+/, $line);
11: $total += @words;
12: }
13: print ("The total number of words is $total\n");
$ program8_12
Here is my test input.
It contains some words.
^D
The total number of words is 9
$

Line 5 reads a line of input from the standard
input file. If this line is empty, the conditional expression
in line 6 is true, and the last statement exits the loop.
(This ensures that the program behaves properly when there are
less than three lines of input.)
Line 7 removes the leading blanks and tabs from this line of input,
and line 8 removes the trailing white space. If the resulting
line is now empty, the line must originally have been blank. Because
this program does not want to include a blank line as one of the
three lines in which to count words, line 9 invokes the redo
statement, which tells the program to start this loop over. The
program returns to line 4, the for statement, but does
not increment the value of $count.
 |
You cannot use the redo statement inside the do statement. Although the do statement behaves like the other control structures, it is actually implemented differently.
|
Note that the redo statement is not recommended, because
it is too easy to lose track of how many times a program goes
through a loop. For example, in Listing 8.12, a quick glance at
the for statement in line 4 seems to indicate that the
program only loops three times; however, the redo statement
might change that.
Listing 8.13 shows an alternative way to solve this problem.
Listing 8.13. A program that counts the words in three non-blank
lines of input without using the redo
statement.
1: #!/usr/local/bin/perl
2:
3: $nonblanklines = 0;
4: while (1) {
5: $line = <STDIN>;
6: last if ($line eq "");
7: $line =~ s/^[\t ]*//;
8: $line =~ s/[\t ]*\n$//;
9: if ($line ne "") {
10: $nonblanklines += 1;
11: @words = split(/[\t ]+/, $line);
12: $total += @words;
13: }
14: last if ($nonblanklines == 3);
15: };
16: print ("The total number of words is $total\n");
$ program8_13
Here is my test input.
It contains some words.
^D
The total number of words is 9.
$

This program is identical to the previous one,
but it is much easier to understand. It uses a more meaningful
variable name-$nonblanklines-which implies that blank
lines are a special case.
As in Listing 8.12, if the line is a blank line, lines 7 and 8
turn it into an empty line by removing all white space. When this
happens, the condition in line 10 fails, and $nonblanklines
is not incremented.
As you've seen, the last, next, and redo
statements enable you to exit a loop from anywhere inside its
statement block, as follows:
while (1) {
$line = <STDIN>;
last if ($line eq "");
}
If the loop is inside another loop, the last, next,
and redo statements quit the inner loop only; for example:
while ($line1 = <FILE1>) {
while ($line2 = <FILE2>) {
last if ($line2 eq "") {
}
}
Here, the last statement only quits the inner while
loop. The outer while loop, which reads from the file
represented by FILE1, continues executing.
To quit from more than one loop at once, do the following:
- Assign a label to the outer loop (the one from which you want
to quit).
- When you use last, next, or redo,
specify the label you just assigned.
Listing 8.14 shows an example of a last statement that
specifies a label.
Listing 8.14. A program that uses a label.
1: #!/usr/local/bin/perl
2:
3: $total = 0;
4: $firstcounter = 0;
5: DONE: while ($firstcounter < 10) {
6: $secondcounter = 1;
7: while ($secondcounter <= 10) {
8: $total++;
9: if ($firstcounter == 4 && $secondcounter == 7) {
10: last DONE;
11: }
12: $secondcounter++;
13: }
14: $firstcounter++;
15: }
16: print ("$total\n");
$ program8_14
47
$

The outer while loop starting in line
5 has the label DONE assigned to it. This label consists
of an alphabetic character followed by one or more alphanumeric
characters or underscores. The colon (:) character following
the label indicates that the label is assigned to the following
statement (in this case, the while statement).
When the conditional expression in line 9 is true, line 10 is
executed. This statement tells the Perl interpreter to jump out
of the loop labeled DONE and continue execution with
the first statement after this loop. (By the way, this code fragment
is just a rather complicated way of assigning 47 to $total.)
 |
Make sure that you do not use a label which has another meaning in Perl. For example, the statement
if: while ($x == 0) { # this is an error in Perl
}
is flagged as erroneous, because the Perl interpreter doesn't realize that the if is not the start of an if statement.
You can avoid this problem by using uppercase letters for label names (such as DONE).
Note that labels can be identical to file variable names:
FILE1: while ($line = <FILE1>) {
...
}
The Perl interpreter has no problem distinguishing the label FILE1 from the file variable FILE1, because it is always possible to determine which is which from the context.
|
You can use next and redo with labels as well,
as shown in the following example:
next LABEL;
redo LABEL;
This next statement indicates that the next iteration
of the loop labeled LABEL is to be executed. This redo
statement indicates that the current iteration of the loop labeled
LABEL is to be restarted.
In a for statement, the expression following the second
semicolon is executed each time the end of the loop is reached
or whenever a next statement is executed. For example:
for ($i = 1; $i <= 10; $i++) {
print ("$i\n");
}
In this example, the expression $i++, which adds 1 to
$i, is executed after the print function is
called.
Similarly, you can define statements that are to be executed whenever
the end of a while loop or an until loop is
reached. To carry out this task, specify a continue statement
after the loop.
$i = 1;
while ($i <= 10) {
print ("$i\n");
}
continue {
$i++;
}
A continue statement must be followed by a statement
block, which is a collection of zero or more statements enclosed
in brace characters. This statement block contains the state-ments
to be executed at the bottom of each loop. In this example, the
statement
$i++;
is executed after each call to print. This while
loop therefore behaves like the for loop you've just
seen.
The continue statement is executed even if a pass through
the loop is prematurely ended by a next statement. It
is not executed, however, if the loop is terminated by a last
statement.
| TIP |
Usually, it is better to use a for statement than to use continue with a while or an until statement, because the for statement is easier to follow.
|
For the sake of completeness, Perl provides a goto statement.
The syntax of the goto statement is
goto label;
label is a label associated with a statement, as defined
in the earlier section, "Using Labeled Blocks for Multilevel
Jumps." The statement to which label is assigned
cannot be in the middle of a do statement or inside a
subroutine. (You'll learn about subroutines on Chapter 9.)
Listing 8.15 is an example of a simple program that uses goto.
Listing 8.15. A program that uses the goto
statement.
1: #!/usr/local/bin/perl
2:
3: NEXTLINE: $line = <STDIN>;
4: if ($line ne "") {
5: print ($line);
6: goto NEXTLINE;
7: }
$ program8_15
Here is a line of input.
Here is a line of input.
^D
$

This program just reads and writes lines of
input until the standard input file is exhausted. If the line
read into $line is not empty, line 6 tells the Perl interpreter
to jump back to the line to which the NEXTLINE label
is assigned, which is line 3.
Note that lines 3-7 are equivalent to the following statement:
print ($line) while ($line = <STDIN>);
| TIP |
There is almost never any need to use the goto statement. In fact, using goto often makes it more difficult to follow the logic of the program. For this reason, using goto is not recommended.
|
ToChapter you learned about the more complex control structures supported
in Perl.
Single-line conditional statements enable you to put a conditional
expression on the same line as the statement to be executed if
the condition is satisfied. This enables you to write more concise
programs.
The for statement enables you to put the loop initializer,
the loop iterator, and the conditional expression together on
the same line. This makes it more difficult to write code that
goes into an infinite loop.
The foreach statement enables a program to loop based
on the contents of a list. When the loop is first executed, the
first element in the list is assigned to a local scalar variable
that is only defined for the duration of the loop. Subsequent
iterations of the loop assign subsequent elements of the list
to this local scalar variable.
The do statement enables you to write a loop that executes
at least once. Its terminating conditional expression appears
at the bottom of the loop, not the top.
The last statement tells the Perl interpreter to exit
the loop and continue execution with the first statement after
the loop. The next statement tells the Perl interpreter
to skip the rest of this iteration of a loop and start with the
next one. The redo statement tells the Perl interpreter
to restart this iteration of a loop. last, next,
and redo cannot be used with the do statement.
You can assign a label to a statement, which enables you to use
last, next, and redo to exit or restart
an outer loop from inside an inner loop.
The continue statement enables you to define code to
be executed each time a loop iterates.
The goto statement enables you to jump to any labeled
statement in your program.
| Q: | Which control structure is the best one to use as a loop?
|
| A: | It depends on what you want to do.
- The foreach structure is the best way to perform operations on every element of a list.
- The for statement is the best way to perform an operation a set number of times.
- The while statement is the best way to perform a loop until a particular condition occurs.
- The do statement is useful if you want to perform a loop at least once. (However, it is not as useful as the others, because you cannot use last, next, or redo with it.)
|
| Q: | Why does Perl bother with the next, last, and redo statements, when the if-elsif-else structure can do the job just as well?
|
| A: | The last and next statements are ideal for loops that check for exceptional conditions. For example:
for ($count = 1; $count <= 3; $count++) {
$line = <STDIN>;
last if ($line eq "");
$line =~ s/^[\t ]+//;
$line =~ s/[\t ]+\n$//;
@words = split(/[\t ]+/, $line);
$total += @words;
}
If the last statement did not exist, the only way to implement this would be with another level of nesting and another condition in the for statement, as follows:
for ($count = 1; $count <= 3 && $line ne ""; $count++) {
$line = <STDIN>;
if ($line ne "") {
$line =~ s/^[\t ]+//;
$line =~ s/[\t ]+\n$//;
@words = split(/[\t ]+/, $line);
$total += @words;
}
}
If your program has to check for several exceptional conditions, you might need several levels of if statements to handle them unless you use next or last.
On the other hand, the redo statement should be avoided whenever possible, because it is difficult to follow program logic when it is used.
|
| Q: | Is the goto statement ever the best way to solve a problem?
|
| A: | Almost never. Avoid using the goto statement if at all possible.
|
| Q: | Why is the conditional expression last in single-line conditional statements?
|
| A: | This is to avoid a problem found in the C programming language. In C, you don't need to put braces around the statement block in a conditional statement if the block consists of only one line. For example, the
following is legal:
if (x == 0)
printf ("x is zero\n");
With this syntax, it is easy to accidentally forget to add the braces when you add another statement to the statement block, as follows:
if (x == 0)
printf ("x is zero\n");
printf ("this statement is always printed\n");
If you glance at this code quickly, you might think that the second call to printf is executed only if x is 0. However, this code is really
if (x == 0)
printf ("x is zero\n");
printf ("this statement is always printed\n");
In Perl, this problem does not exist because the only way to write the first statement is
print ("x is zero\n") if (x == 0);
|
| Q: | Is a continue block executed if a redo statement restarts the loop?
|
| A: | No. The continue block is executed only when an iteration of a loop is successfully completed (by reaching the bottom of a loop or a next statement).
|
The Workshop provides quiz questions to help you solidify your
understanding of the material covered and exercises to give you
experience in using what you've learned. Try and understand the
quiz and exercise answers before you go on to tomorrow's lesson.
- How many times does the following loop iterate?
for ($count = 0; $count < 7; $count++) {
print ("$count\n");
}
- How many times does the following loop iterate?
$count = 1;
do {
print ("$count\n");
} until ($count++ > 10);
- How many times does the following loop iterate?
for ($count = 1; $count <= 10; $count++) {
last if ($count == 5);
}
- How many times does the following loop iterate?
$restart = 0;
for ($count = 1; $count <= 5; $count++)
{
redo if ($restart++ == 1);
}
- Write a single-line conditional statement that quits a loop
if $x equals done.
- Write a single-line conditional statement that restarts a
loop if the first element of the list @list is 26.
- Write a single-line conditional statement that goes to the
next iteration of the loop labeled LABEL if $scalar
equals #.
- Write a single-line conditional statement that prints the
digits from 1 to 10. (Use a scalar variable, and assume that it
has not been previously defined.)
- What does the continue statement do?
- Write a program that uses the do statement to print
the numbers from 1 to 10.
- Write a program that uses the for statement to print
the numbers from 1 to 10.
- Write a program that uses a loop to read and write five lines
of input. Use the last statement to exit the loop if
there are less than five lines to read.
- Write a program that loops through the numbers 1 to 20, printing
the even-numbered values. Use the next statement to skip
over the odd-numbered values.
- Write a program that uses the foreach statement to
check each word in the standard input file. Print the line numbers
of all occurrences of the word the (in uppercase, lowercase,
or mixed case).
- Write a program that uses a while loop and a continue
statement to print the integers from 10 down to 1.
- BUG BUSTER: What is wrong with the following code?
$count = 1;
do {
print ("$count\n");
last if ($count == 10);
$count++;
} while (1);

|