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.
You see how to use the if statement to 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.
Decision statements use the if keyword to execute a statement block based on the evaluation of an expression or to choose between executing one of two statement blocks based on the evaluation of an expression. They are used quite often. For example, a program might need to run one code section if a customer is female and another code section if the customer is male.
The syntax for the if statement is the following:
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.
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.
Assign $firstVar a value of 10.
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 also can use the if statement to select among multiple statement blocks. The if...elsif form of the statement is used for this purpose.
Initialize $month to 2.
If the value of $month is 1, then print January.
If the value of $month is 2, then print February.
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.
A loop is used to repeat the execution of a statement block until a certain condition is reached. A loop can be used to iterate through an array looking for a value. Loops also can be used to count quantities. Actually, the number of uses for loops is pretty much unlimited. There are three types of loops: while loops, until loops, and for loops.
While loops are used to repeat a block of statements while some condition is true. There are two forms of the loop: one where the condition is checked before the statements are executed (the do..while loop), and one in which the condition is checked after the statements are executed (the while loop).
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 rarely is used. However, you can see it demonstrated in the section, "Example: Using the -n and -p Options," in Chapter 17, "Using 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.
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.
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 always will be pages to process. Notice that because 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.
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:
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.
Until loops are used to repeat a block of statements while some condition is false. Like the previous while loop, there are also two forms of the until loop: one where the condition is checked before the statements are executed (the do...until loop), and one in which the condition is checked after the statements are executed (the until loop).
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.
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 paren-theses and add == 1 to the right-hand side. The above loop then becomes |
do { ... } until (($firstVar < 2) == 1);
This example shows that the statement block is executed even though the condition $firstVar < 2 is false when the loop starts. The next example shows the until loop in action, which does not execute the statement block when the conditional expression is false when the loop starts.
Initialize $firstVar to 10.
Start the until loop and test the condition. If true, don't execute the state-ment 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 an until loop that shows the statement block getting executed:
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
One of the most common tasks in programming is looping a specific number of times. Whether you need to execute a certain fuNCtion for every customer in your database or print a page in a report, the for loop can be used. Its syntax is:
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:
Start the for loop by initializing $firstVar to zero. The $firstVar variable will be iNCremented each time the statement block is executed. 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 also can be used to count backwards.
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.
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. 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.
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. 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.
Arrays are so useful that Perl provides a special form of the for statement just for them. The foreach statement is used solely to iterate over the elements of an array. It is very handy for finding the largest element, printing the elements, or simply seeing if a given value is a member of an array.
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.
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 value 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. 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.
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 also could 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 purpose 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.
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. |
Perl has four keywords that let you change the flow of your programs.
Table 7.1 lists the keywords along with a short description.
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.
The last keyword is used to exit from a statement block. This ability is useful if you are searching an array for a value. When the value is found, you can stop the loop early.
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 theiNCrement/decrement expression are not reevaluated, the statement block is left. Execution begins again immediately after the ending curly brace.
You also can 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:
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.
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 repeats 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 also was exited because the outer print statement is never executed.
The next keyword lets you skip the rest of the statement block and start the next iteration. One use of this behavior could be to select specific array elements for processing and ignoring the rest. For example:
Create an array of 10 elements.
Print the array.
Iterate over the array.
Ignore the third and fifth 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 third and fifth, to asterisks regardless of their former values. The next keyword forces Perl to skip over the assignment statement and go directly to the iNCrement/decrement expression. You also can use the next keyword in nested loops.
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.
The redo keyword causes Perl to restart the current statement block. Neither the iNCrement/decrement expression nor the conditional expression is evaluated before restarting the block. This keyword is usually used when getting input from outside the program, either from the keyboard or from a file. It is essential that the conditions that caused the redo statement to execute can be changed so that an endless loop does not occur.
This example will demonstrate the redo keyword with some keyboard input:
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 uppercase 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:
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 uppercase 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. Because 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 goto statement lets your program jump directly to any label. However, because Perl also provides the loop statements and other jump keywords, its use is looked down on by most programmers. Using the goto in your programs frequently causes your program logic to become convoluted. If you write a program that you feel needs a goto in order to run, then use it-but first, try to restructure the program to avoid it.
This chapter was devoted to learning about three types of statements: decision, loop, and jump. Decision statements use the if keyword to execute a statement block depending on the evaluation of conditional expressions. Loop statements also execute a statement block based on a given condition, but they will repeatedly execute the block until the condition is true or while the condition is true. Jump statements are used to restart statement blocks, skip to the next iteration in a loop, and exit loops prematurely.
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, a 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.
Answers to Review Questions are in Appendix A.