| |||
Links Sections Chapters Part I: Basic Perl 02-Numeric and String
Literals Part II: Intermediate Perl Part III: Advanced Perl 13-Handling Errors and
Signals Part IV: Perl and the Internet 21-Using Perl with Web
Servers Appendixes |
The last chapter, "Statements," discussed no-action, action, and modified statements. This chapter discusses three more types of statements: decision statements, loop statements, and jump statements.
The if statement lets you decide on one or more courses of actions. Loop statements are used to repeat a series of statements until a given condition is either true or false. And finally, we'll wrap up the chapter by looking at jump statements which let you control program flow by moving directly to the beginning or the end of a statement block.
if (CONDITION) {
# Code block executed
# if condition is true.
} else {
# Code block executed
# if condition is false.
}
Sometimes you need to choose from multiple statement blocks, such as
when you need to execute a different statement block for each month. You use the
if...elsif statement for this type of decision. The if...elsif
statement has this syntax:
if (CONDITION_ONE) {
# Code block executed
# if condition one is true.
} elsif (CONDITION_TWO) {
# Code block executed
# if condition two is true.
} else {
# Code block executed
# if all other conditions are false.
}
Conditional expressions can use any of the operators discussed in
Chapter 4, "Operators." Even
assignment operators can be used because the value of an assignment expression
is the value that is being assigned. That last sentence may be a bit confusing
so let's look at an example.
Pseudocode |
Assign $firstVar a value of 10. Subtract five from $firstVar and if the resulting value is true (for instance, not zero) then execute the statement block. |
$firstVar = 10;
if ($firstVar -= 5) {
print("firstVar = $firstVar\n");
}
This program displays:
firstVar = 5
Tip |
If you're a C or C++ programmer, take heed: the curly braces around the statement block are not optional in Perl. Even one-line statement blocks must be surrounded by curly braces. |
This example, in addition to demonstrating the use of assignment operators inside conditional expressions, also shows that the else part of the if statement is optional. If the else part was coded, then it would only be executed when $firstVar starts out with a value of 5.
Pseudocode |
Assign $firstVar a value of 5. Subtract five from $firstVar and if the resulting value is true (in other words, not zero) then print $firstVar. If not, print "firstVar is zero". |
$firstVar = 5;
if ($firstVar -= 5) {
print("firstVar = $firstVar\n");
} else {
print("firstVar is zero\n");
}
This program displays:
firstVar is zero
This example shows the use of the else
clause of the if statement. Because the value of $firstVar
minus 5 was zero the statements in the else clause were executed.
You can also use the if statement to select among multiple statement blocks. The if...elsif form of the statement is used for this purpose.
Pseudocode |
Initialize $month to 2. If the value of $month is 1 then print January. Or else if the value of $month is 2 then print February. Or else if the value of $month is 3 then print March. For every other value of $month, print a message. |
$month = 2;
if ($month == 1) {
print("January\n");
}
elsif ($month == 2) {
print("February\n");
}
elsif ($month == 3) {
print("March\n");
}
else {
print("Not one of the first three months\n");
}
This program displays:
February
The else clause at the end of the elsif
chain serves to catch any unknown or unforeseen values and is a good place to
put error messages. Frequently, those error messages should include the errant
value and be written to a log file so that the errors can be evaluated. After
evaluation, you can decide if the program needs to be modified to handle that
unforeseen value using another elsif clause.
The do...while loop has this syntax:
do {
STATEMENTS
} while (CONDITION);
The while loop has this syntax:
while (CONDITION) {
STATEMENTS
}
continue {
STATEMENTS
}
The statements in the continue block of the while loop are
executed just before the loop starts the next iteration. The continue
block is rarely used. However, you can see it demonstrated in section "Example:
Using the -n and -p Options" in Chapter 17, "Command-line Options."
Which type you use for any particular task is entirely dependent on your needs at the time. The statement block of a do...while loop always will be executed at least once. This is because the condition is checked after the statement block is executed rather than before. Here is an example of the do...while loop.
Pseudocode |
Initialize $firstVar to 10. Start the do...while loop. Print the value of $firstVar. Increment $firstVar. Check the while condition, if true jump back to the start of the statement block. Print the value of $firstVar. |
$firstVar = 10;
do {
print("inside: firstVar = $firstVar\n");
$firstVar++;
} while ($firstVar < 2);
print("outside: firstVar = $firstVar\n");
This program displays:
inside: firstVar = 10
outside: firstVar = 11
This example shows that the statement block is
executed even though the condition $firstVar < 2 is false when the
loop starts. This ability occasionally comes in handy while counting down - such
as when printing pages of a report.
Pseudocode |
Initialize $numPages to 10. Start the do...while loop. Print a page. Decrement $numPages and then loop if the condition is still true. |
$numPages = 10;
do {
printPage();
} while (--$numPages);
When this loop is done, all of the pages will
have been displayed. This type of loop would be used when you know that there
will always be pages to process. Notice that since the predecrement operator is
used, the $numPages variable is decremented before the condition
expression is evaluated.
If you need to ensure that the statement block does not get executed then you need to use the while statement.
Pseudocode |
Initialize $firstVar to 10. Start the while loop and test the condition. If false, don't execute the statement block. Print the value of $firstVar. Increment $firstVar. Jump back to the start of the statement block and test the condition again. Print the value of $firstVar. |
$firstVar = 10;
while ($firstVar < 2) {
print("inside: firstVar = $firstVar\n");
$firstVar++;
};
print("outside: firstVar = $firstVar\n");
This program displays:
outside: firstVar = 10
This example shows that the statement block
is never evaluated if the condition is false when the while loop
starts. Of course, it's more common to use while loops that actually
execute the statement block - like the following:
Pseudocode |
Initialize $firstVar to 10. Start the while loop and test the condition. Print the value of $firstVar. Increment $firstVar. Jump back to the start of the statement block and test the condition again. Print the value of $firstVar. |
$firstVar = 10;
while ($firstVar < 12) {
print("inside: firstVar = $firstVar\n");
$firstVar++;
};
print("outside: firstVar = $firstVar\n");
This program displays:
inside: firstVar = 10
inside: firstVar = 11
outside: firstVar = 12
It's important to note that the value of
$firstVar ends up as 12 and not 11 as you might expect upon casually
looking at the code. When $firstVar is still 11, the condition is true,
so the statement block is executed again, thereby incrementing
$firstVar to 12. Then, the next time the condition is evaluated, it is
false and the loop ends with $firstVar equal to 12.
The do...until loop has this syntax:
do {
STATEMENTS
} until (CONDITION);
The until loop has this syntax:
until (CONDITION) {
STATEMENTS
}
Again, the loop type you use is dependent on your needs at the time.
Here is an example of the do...until loop.
Pseudocode |
Initialize $firstVar to 10. Start the do..until loop. Print the value of $firstVar. Increment $firstVar. Check the until condition, if false jump back to the start of the statement block. Print the value of $firstVar. |
$firstVar = 10;
do {
print("inside: firstVar = $firstVar\n");
$firstVar++;
} until ($firstVar < 2);
print("outside: firstVar = $firstVar\n");
This program displays:
inside: firstVar = 10
inside: firstVar = 11
inside: firstVar = 12
inside: firstVar = 13
inside: firstVar = 14
...
This loop continues forever because the condition can never be
true. $firstVar starts out greater than 2 and is incremented inside the
loop. Therefore, this is an endless loop.
Tip |
If you ever find it hard to understand a conditional
expression in a loop statement, try the following: Wrap the entire
condition expression inside parentheses and add == 1 to the right-hand
side. So the above loop becomes
do { ... } until (($firstVar < 2) == 1); |
Pseudocode |
Initialize $firstVar to 10. Start the until loop and test the condition. If true, don't execute the statement block. Print the value of $firstVar. Increment $firstVar. Jump back to the start of the statement block and test the condition again. Print the value of $firstVar. |
$firstVar = 10;
until ($firstVar < 20) {
print("inside: firstVar = $firstVar\n");
$firstVar++;
};
print("outside: firstVar = $firstVar\n");
This program displays:
outside: firstVar = 10
This example shows that the statement block
is never evaluated if the condition is true when the until loop starts.
Here is another example of a until loop that shows the statement block
getting executed.
Pseudocode |
Initialize $firstVar to 10. Start the while loop and test the condition. Print the value of $firstVar. Increment $firstVar. Jump back to the start of the statement block and test the condition again. Print the value of $firstVar. |
$firstVar = 10;
until ($firstVar > 12) {
print("inside: firstVar = $firstVar\n");
$firstVar++;
};
print("outside: firstVar = $firstVar\n");
This program displays:
inside: firstVar = 10
inside: firstVar = 11
inside: firstVar = 12
outside: firstVar = 13
for (INITIALIZATION; CONDITION; INCREMENT/DECREMENT) {
STATEMENTS
}
The initialization expression is executed first - before the
looping starts. It can be used to initialize any variables that are used inside
the loop. Of course, this could be done on the line before the for
loop. However, including the initialization inside the for statement
aids in identifying the loop variables.
When initializing variables, be sure not to confuse the equality operator (==) with the assignment operator (=). The following is an example of what this error could look like:
for ($index == 0; $index < 0; $index++)
One of the equal signs
should be removed. If you think you are having a problem with programming the
for loop, make sure to check out the operators.
The condition expression is used to determine whether the loop should continue or be ended. When the condition expression evaluates to false, the loop will end.
The increment/decrement expression is used to modify the loop variables in some way each time the code block has been executed. Here is an example of a basic for loop.
Pseudocode |
Start the for loop by initializing $firstVar to zero. The $firstVar variable will be incremented each time the statement block is executed. And the statement block will be executed as long as $firstVar is less than 100. Print the value of $firstVar each time through the loop. |
for ($firstVar = 0; $firstVar < 100; $firstVar++) {
print("inside: firstVar = $firstVar\n");
}
This program will display:
inside: firstVar = 0
inside: firstVar = 1
...
inside: firstVar = 98
inside: firstVar = 99
This program will display the numbers 0 through
99. When the loop is over, $firstVar will be equal to 100.
For loops can also be used to count backwards.
Pseudocode |
Start the for loop by initializing $firstVar to 100. The $firstVar variable will be decremented each time the statement block is executed. And the statement block will be executed as long as $firstVar is greater than 0. Print the value of $firstVar each time through the loop. |
for ($firstVar = 100; $firstVar > 0; $firstVar--) {
print("inside: firstVar = $firstVar\n");
}
This program will display:
inside: firstVar = 100
inside: firstVar = 99
...
inside: firstVar = 2
inside: firstVar = 1
You can use the comma operator to evaluate two
expressions at once in the initialization and the increment/decrement
expressions.
Pseudocode |
Start the for loop by initializing $firstVar to 100 and $secondVar to 0. The $firstVar variable will be decremented and $secondVar will be incremented each time the statement block is executed. And the statement block will be executed as long as $firstVar is greater than 0. Print the value of $firstVar and $secondVar each time through the loop. |
for ($firstVar = 100, $secondVar = 0;
$firstVar > 0;
$firstVar--, $secondVar++) {
print("inside: firstVar = $firstVar secondVar = $secondVar\n");
}
This program will display:
inside: firstVar = 100 secondVar = 0
inside: firstVar = 99 secondVar = 1
...
inside: firstVar = 2 secondVar = 98
inside: firstVar = 1 secondVar = 99
Note |
The comma operator lets you use two expressions where Perl would normally let you have only one. The value of the statement becomes the value of the last expression evaluated. |
A more common use of the comma operator might be to initialize some flag variables that you expect the loop to change. This next example will read the first 50 lines of a file. If the end of the file is reached before the last line is read, the $endOfFile flag variable will be set to 1.
Pseudocode |
Start the for loop by initializing the end of file flag variable to zero to indicate false then set $firstVar to 0. The $firstVar variable will be incremented each time the statement block is executed. And the statement block will be executed as long as $firstVar is less than 50. Print the value of $firstVar and $secondVar each time through the loop. |
for ($endOfFile = 0, $firstVar = 0; $firstVar < 50;
$firstVar++, $secondVar++) {
if (readLine() == 0)
$endOfFile = 1;
}
If the $endOfFile variable is 1 when the loop ends then you
know the file has less than 50 lines.
foreach LOOP_VAR (ARRAY) {
STATEMENTS
}
The loop variable is assigned the value of each array element, in
turn until the end of the array is reached. Let's see how to use the
foreach statement to find the largest array element.
Pseudocode |
Call the max() function twice with different parameters each time. Define the max() function. Create a local variable, $max, then get the first element from the parameter array. Loop through the parameter array comparing each element to $max. If the current element is greater than $max. Return the value of $max. |
print max(45..121, 12..23) . "\n";
print max(23..34, 356..564) . "\n";
sub max {
my($max) = shift(@_);
foreach $temp (@_) {
$max = $temp if $temp > $max;
}
return($max);
}
This program displays:
121
564
There are a couple of important things buried in this example. One is the use of the shift() function to assign a value to a local variable and remove the first element of the parameter array from the array at the same time. If you use shift() all by itself, the value of the first element is lost.
The other important thing is the use of $temp inside the foreach loop. Some Perl programmers dislike using temporary variables in this manner. When the foreach loop is over, the local $temp variable is destroyed. Perl has an internal variable, $_, that can be used instead. If no loop variable is specified, $_ will be assigned the value of each array element as the loop iterates.
Pseudocode |
Print the return value from the max() function. Define the max() function. Create a local variable, $max, then get the first element from the parameter array. Loop through the parameter array comparing each element to $max. If the current element is greater than $max: Return the value of $max. |
print max(45..121, 12..23) . "\n";
print max(23..34, 356..564) . "\n";
sub max {
my($max) = shift(@_);
foreach (@_) {
$max = $_ if $_ > $max;
}
return($max);
}
The third item has nothing to do with the foreach loop, at
least not directly. But this seems like a good time to mention it. The statement
inside the loop could also be written in the following way:
$max = $_ if $max < $_;
with the sense of the operator
reversed. However, notice that it will take more effort to understand what the
statement - as a whole - is doing. The reader of your program knows that the
function is looking for the greatest value in a list. If the less than operator
is used, it will contradict the stated purposed of your function - at least
until the reader figures out the program logic. Whenever possible, structure
your program logic to agree with the main premise of the function.
Now for the fourth, and final, item regarding this small program. Notice that the function name and the local variable name are the same except for the beginning dollar sign. This shows that function names and variable names use different namespaces.
Remember namespaces? They were mentioned in Chapter 3, "Variables".
Using the foreach statement requires using a little bit of caution because the local variable (either $_ or the one you specify) accesses the array elements using the call by reference scheme. When call by reference is used, changing the value in one place (such as inside the loop) also changes the value in the main program.
Pseudocode |
Create an array from 1 to 10 with 5 repeated. Print the array. Loop through the array replacing any elements equal to 5 with "**". Print the array. |
@array = (1..5, 5..10);
print("@array\n");
foreach (@array) {
$_ = "**" if ($_ == 5);
}
print("@array\n");
This program displays:
1 2 3 4 5 5 6 7 8 9 10
1 2 3 4 ** ** 6 7 8 9 10
Caution |
If you use the foreach loop to change the value of the array elements, be sure to comment your code to explain the situation and why this method was used. |
Keywords | Description |
---|---|
last | Jumps out of the current statement block. |
next | Skips the rest of the statement block and continues with the next iteration of the loop. |
redo | Restarts the statement block. |
goto | Jumps to a specified label. |
Each of these keywords is described further in its own section which follows.
Pseudocode |
Create an array holding all 26 letters. Use a for loop to iterate over the array. The index variable will start at zero and increment while it is less than the number of elements in the array. Test the array element to see if it is equal to "T". Notice that the string equality operator is used. If the array element is "T", then exit the loop. |
@array = ("A".."Z");
for ($index = 0; $index < @array; $index++) {
if ($array[$index] eq "T") {
last;
}
}
print("$index\n");
This program displays:
19
This loop is straightforward except for the way that it
calculates the number of elements in the array. Inside the conditional
expression, the @array variable is evaluated in an scalar context. The
result is the number of elements in the array.
When the last keyword is executed, the conditional expression and the increment/decrement expression are not re-evaluated, the statement block is just left. Execution begins again immediately after the ending curly brace.
You can also use a label with the last keyword to indicate which loop to exit. A label is a name followed by a colon. Labels names usually use all capital letters, but Perl does not insist on it. When you need to exist a nested loop, labels are a big help. Let's look at this situation in two steps. Here is a basic loop.
Pseudocode |
Loop from 0 to 10 using $index as the loop variable. If $index is equal to 5 then exit the loop. Print the value of $index while inside the loop. Print the value of $index after the loop ends. |
for ($index = 0; $index < 10; $index++) {
if ($index == 5) {
last;
}
print("loop: index = $index\n");
}
print("index = $index\n");
This program displays:
loop: index = 0
loop: index = 1
loop: index = 2
loop: index = 3
loop: index = 4
index = 5
So far, pretty simple. The print statement inside the loop
lets us know that the $index variable is being incremented. Now, let's
add an inner loop to complicate things.
Pseudocode |
Specify a label called OUTER_LOOP. Loop from 0 to 10 using $index as the loop variable. If $index is equal to 5 then exit the loop. Start an inner loop that repeat while $index is less than 10. If $index is 4 then exit out of both inner and outer loops. Increment $index. Print the value of $index. |
OUTER_LOOP:
for ($index = 0; $index < 10; $index++) {
if ($index == 5) {
last;
}
while ($index < 10) {
if ($index == 4) {
last OUTER_LOOP;
}
print("inner: index = $index\n");
$index++;
}
print("outer: index = $index\n");
}
print("index = $index\n");
This program displays:
inner: index = 0
inner: index = 1
inner: index = 2
inner: index = 3
index = 4
The inner while loop increments $index
while it is less than 10. However, before it can reach 10 it must pass 4, which
triggers the if statement and exits both loops. You can tell that the
outer loop was also exited because the outer print statement is never executed.
Pseudocode |
Create an array of 10 elements. Print the array. Iterate over the array. Ignore the 3rd and 5th element. Change the current element to an asterisk. Print the array to verify that it has been changed. |
@array = (0..9);
print("@array\n");
for ($index = 0; $index < @array; $index++) {
if ($index == 3 || $index == 5) {
next;
}
$array[$index] = "*";
}
print("@array\n");
This program displays:
0 1 2 3 4 5 6 7 8 9
* * * 3 * 5 * * * *
This example changes every array element, except
the 3rd and 5th, to asterisks regardless of their former value. The
next keyword forces Perl to skip over the assignment statement and go
directly to the increment/decrement expression. You can also use the
next keyword in nested loops.
Pseudocode |
Define a label called OUTER_LOOP. Start a for loop that iterates from 0 to 3 using $row as the loop variable. Start a for loop that iterates from 0 to 3 using $col as the loop variable. Display the values of $row and $col and mention that the code is inside the inner loop. If $col is equal to 1, start the next iteration of loop near the label OUTER_LOOP. Display the values of $row and $col and mention that the code is inside the outer loop. |
OUTER_LOOP: for ($row = 0; $row < 3; $row++) {
for ($col = 0; $col < 3; $col++) {
print("inner: $row,$col\n");
if ($col == 1) {
next OUTER_LOOP;
}
}
print("outer: $row,$col\n\n");
}
This program displays:
inner: 0,0
inner: 0,1
inner: 1,0
inner: 1,1
inner: 2,0
inner: 2,1
You can see that the next statement in the inner
loop causes Perl to skip the print statement in the outer loop whenever
$col is equal to 1.
This example will demonstrate the redo keyword with some keyboard input.
Pseudocode |
Start a statement block. Print a prompt asking for a name. Read a string from the keyboard. Control is returned to the program when the user of the program presses the enter key. Remove the newline character from the end of the string. If the string has zero length it means the user simply pressed the enter key without entering a name, so display an error message and redo the statement block. Print a thank you message with the name in upper-case characters. |
{
print("What is your name? ");
$name = <STDIN>;
chop($name);
if (! length($name)) {
print("Msg: Zero length input. Please try again\n");
redo;
}
print("Thank you, " . uc($name) . "\n");
}
Tip |
It's worth noting that the statement block in this example acts like a single-time loop construct. You can use any of the jump keywords inside the statement block. |
The redo statement helps you to have more straightforward program flow. Without it, you would need to use a do...until loop. For example:
Pseudocode |
Start a do...until statement. Print a prompt asking for a name. Read a string from the keyboard. Control is returned to the program when the user of the program presses the enter key. Remove the newline character from the end of the string. If the string has zero length it means the user simply pressed the enter key without entering a name, so display an error message. Evaluate the conditional expression. If true, then the user entered a name and the loop can end. Print a thank you message with the name in upper-case characters. |
do {
print("What is your name? ");
$name = <STDIN>;
chomp($name);
if (! length($name)) {
print("Msg: Zero length input. Please try again\n");
}
} until (length($name));
print("Thank you, " . uc($name) . "\n");
The do...until loop
is less efficient because the length of $name needs to be tested twice.
Since Perl has so many ways to do any given task, it pays to think about which
method is more efficient before implementing your ideas.
The if statement can be used with an else clause to choose one of two statement blocks to execute. Or you can use the elsif clause to choose from among more than two statement blocks.
Both the while and until loop statements have two forms. One form (the do... form) executes a statement block and then tests a conditional expression and the other form tests the condition before executing the statement block.
The for loops are the most complicated type of loop because they involve three expressions in addition to a statement block. There is an initialization expression, an conditional expression, and an increment/decrement expression. The initialization expression is evaluated first, then the conditional expression. If the conditional expression is false, the statement block is executed. Next the increment/decrement expression is evaluated and the loop starts again with the conditional expression.
Foreach loops are used to iterate through an array. Each element in the array is assigned to a local variable as the loop progresses through the array. If you don't specify a local variable, Perl will use the $_ special variable. You need to be careful when changing the value of the local variable because it uses the call by reference scheme. Therefore, any change to the local variable will be reflected in the value of the array element outside the foreach loop.
The last keyword is used to jump out of the current statement block. The next keyword is used to skip the rest of the statement block and continue to the next iteration of the loop. The redo keyword is used to restart the statement block. And finally, the goto keyword should not be used because the other jump keywords are more descriptive. All of the jump keywords can be used with labels so they can be used inside nested loops.
$firstVar = 5;
{
if ($firstVar > 10) {
last;
}
$firstVar++;
redo;
}
print("$firstVar\n");