MOVED TO CODEWALKERS FEB 2012 Control Flow Constructs (edited – lorraine)

Beginning Perl, Second Edition

Written by James Lee

Note – fix tables & empty spaces

Published by Apress

Chapter 3



Most of the programs we have seen so far have been very simply structured—they’ve done one statement after another in turn. If we were to represent statements by boxes, our programs would look like this:

This sort of diagram is called a flow chart, and programmers have used them for a long time to help design their programs. They’re considered a bit passé these days, but they’re still useful. The path Perl (or any other language) takes by following the arrows is called the flow of execution of the program. Boxes denote statements (or a single group of statements), and diamonds denote tests. There are also a whole host of other symbols for magnetic tapes, drum storage, and all sorts of wonderful devices, now happily lost in the mists of time.

We can choose our path through the program depending on certain things. For instance, we’ll do something if two strings are equal:

We can also iterate, or loop, through a number of things by executing a block of statements again and again for each element of the list:

We’ll take a look at the other sorts of control structures we have in Perl. For example, structures that do things if or unless something is true. We’ll see structures that do things while something is true, or until it is true. Structures that loop for a certain number of times, or foreach element in a list. Each of the words in italic in this paragraph is a Perl keyword, and we’ll examine them in this chapter.

The if Statement

In programming, we often need to test a condition, and if that condition is true, take some action. This can be performed using an if statement. The if has the general syntax:

if ( condition ) {
   statements
}

Don’t type this in and try to make it run—it is meant to be a general structure of the construct.

An important note: those curly braces around the body (the statements) are required. You must use them, even if the body is one line of code.

For instance, let’s say we want to divide by a number unless that number is 0. We can first check to see if the number is not 0, and if it is not, perform the division.

if ($number != 0) {
   $result = 100 / $number;
}

Let’s create a program to use the if statement. It will prompt the user to enter a number. If the number is not 0, then 100 is divided by that number and the result is stored in $result . If the number is 0, the result will remain the default value of 0:

#!/usr/bin/perl -w
# if.pl

use strict;

print "please enter a number: " ;
chomp(my $number = <STDIN>);
my $result = 0;
if ($number != 0) {
  
$result = 100 / $number;
}

print "the result is: $resultn";

Recall that the statement

chomp(my $number = <STDIN>);

is shorthand for the two statements that read from standard input and then remove the newline:

my $number = <STDIN>;
chomp $number;

Now let’s execute the program, once with a nonzero value and once with 0:

$ perl if.pl
please enter a number: 8
the result is: 12.5
$ perl if.pl
please enter a number: 0
the result is: 0
$

Operators Revisited

The if statement, and all the other control structures we’re going to visit in this chapter, test to see if a condition is true or false. They do this using the Boolean logic mentioned in Chapter 2, together with Perl’s ideas of true and false. To remind you of these:

  1. An empty string, "" , is false.
  2. The number 0 and the string "0" are both false. 
     
  3. An empty list, () , is false. 
     
  4. The undefined value is false. 
     
  5. Everything else is true.

However, you need to be careful for a few traps here. A string containing invisible charac ters, like spaces or newlines, is true. A string that isn’t "0" is true, even if its numerical value is 0, so "0.0" for instance, is true.

Larry Wall has said that programming Perl is an empirical science—you learn things about it by trying them out. Is (()) a true value? You can look it up in books and the online documentation, or you can spend a few seconds writing a program like this:

#!/usr/bin/perl – w
# emptylist.pl

use strict;

if ( (()) ) {
  
print "Yes, it is.n";
}

This way you get the answer straight away, with the minimum of fuss. (If you’re interested, it isn’t a true value.) We’ve also seen that conditional operators can test things out, returning 1 if the test was successful and empty string if it was not. Let’s see more of the things we can test.

Comparing Numbers

We can test whether one number is bigger, smaller, or the same as another. Assuming we have two numbers stored in the variables $x and $y , Table 3-1 shows the operators we can use for this.

Table 3-1. Numeric Comparison Operators  

Operator

Description

$x > $y

$x is greater than $y.

$x < $y

$x is less than $y.

$x >= $y

$x is greater than or equal to $y.

$x <= $y

$x is less than or equal to $y.

$x == $y

$x has the same numeric value as $y.

$x != $y

$x does not have the same numeric value as $y.

Don’t forget that the numeric comparison needs a doubled equals sign ( == ) so that Perl doesn’t think you’re trying to set $x to the value of $y .

Also remember that Perl converts $x and $y to numbers in the usual way. It reads numbers or decimal points from the left for as long as possible, ignoring initial spaces, and then drops the rest of the string. If no numbers were found, the value is set to 0.

Let’s see an example—a very simple guessing game. The computer has a number, and the user has to guess what it is. If the user doesn’t guess correctly, the computer gives a hint. As we learn more about Perl, we’ll add the opportunity to give more than one try, and to pick a different number each game.

#!/usr/bin/perl -w
# guessnum1.pl

use strict;

my $target = 12;
print "Guess my number!n";
print "Enter your guess: ";
my $guess = <STDIN>;
if ($target == $guess) {
   print "That’s it! You guessed correctly!n";
   exit;
}
if ($guess > $target) {
   print "Your number is more than my numbern";
   exit;
}
if ($guess < $target){
   print "Your number is less than my numbern";
   exit;
}

Let’s have a few goes at it:

$ perl guessnum1.pl
Guess my number!
Enter your guess: 3
Your number is less than my number
$ perl guessnum1.pl
Guess my number!
Enter your guess: 15
Your number is more than my number
$ perl guessnum1.pl
Guess my number!
Enter your guess: 12
That’s it! You guessed correctly!
$

The first thing we do in this program is set up our secret number. OK, at the moment it’s not very secret, since it’s right there in the source code, but we can improve on this later. After this, we get a number from the user:

my $guess = <STDIN>;

Then we do three sorts of comparisons with the numeric operators we’ve just seen. We use the basic pattern of the if statement again: if ( condition ) { action } .

if ($target == $guess) {
   print "That’s it! You guessed correctly!n";
   exit;
}

Since only one of the tests can be true—the user’s number can’t be both smaller than our number and the same as it—we may as well stop work after a test was successful. The exit() function tells Perl to stop the program completely.

Comparing Strings

When we’re comparing strings, we use a different set of operators to do the comparisons as listed in Table 3-2.

Table 3-2. String Comparison Operators

Operator Description
$x gt $y$x is string greater than $y .
$x lt $y$x is string less than $y .
$x ge $y$x is string greater than or equal to $y .
$x le $y$x is string less than or equal to $y .
$x eq $y$x is the same as $y .
$x ne $y$x is not the same as $y .

 

 

 

 

 

 

 

Here’s a very simple way of testing if a user knows a password. Note: don’t use a good password in this program since the user can just read the source code to find it!

#!/usr/bin/perl – w
# password.pl

use strict;

my $password = "foxtrot";
print "Enter the password: ";
my $guess = <STDIN>;
chomp $guess;
if ($password eq $guess) {
  
print "Pass, friend.n";
}
if ($password ne $guess) {
  
die "Go away, imposter!n";
}

Here’s our security system in action:

$ perl password.pl
Enter the password: abracadabra
Go away, imposter!
$ perl password.pl
Enter the password: foxtrot
Pass, friend.
$

This program starts by asking the user for input:

my $guess = <STDIN>;

Just a warning: this is a horrendously bad way of asking for a password, since it’s echoed to the screen, and everyone looking at the user’s computer would be able to read it. Even though you won’t be using a program like this, if you ever do need to get a password from the user, the Perl FAQ provides a better method in perlfaq8. Type perldoc -q password to find it.

chomp $guess;

This statement chomp s the newline off of $guess . We must never forget to remove the newline from the end of the user’s data. We didn’t need to do this for numeric comparison, because Perl would remove that for us anyway during conversion to a number. Otherwise, even if the user had entered the right password, Perl would have tried to compare "foxtrot" with "foxtrotn" and it could never be the same.

if ($password ne $guess) {
   die "Go away, imposter!n";
}

Then if the password we have isn’t the same as the user’s input, we send out a rude message and terminate the program.

Other Tests

What other tests can we perform? We can test if a variable is defined (it must contain something other than the undefined value) using defined() .

#!/usr/bin/perl -w
# defined.pl

use strict;

my ($a, $b);
$b = 10;

if (defined $a) {
  
print "$a has a value.n";
}
if (defined $b) {
   print "$b has a value.n";
}

Not surprisingly, the result we get is this:

$ perl defined.pl
$b has a value.
$

You can use this to avoid the warnings you get when you try and use a variable that doesn’t have a value. If we’d tried to say if ($a == $b) , Perl would have said

Use of uninitialized value in numeric eq (==)

So we have our basic comparisons. Don’t forget that some functions will return a true value if they were successful and false if they were not. You will often want to check whether the return value of an operation (particularly one that relates to the operating system) is true or not.

Logical Operators

We also saw in Chapter 2 that we can join together several tests into one by the use of the logical operators. Table 3-3 provides a summary of those.

Table 3-3. Logical Operators  

Operator

Description

$x and $y

True if both $x and $y are true

$x && $y

 

$x or $y

True if either of $x or $y, or both are true

$x || $y

 

not $x

True if $x is not true

! $x

 

The operators and , or , and not are usually used instead of && , || , and ! mainly due to their readability. The operator not means not, after all. Don’t forget there is a difference in precedence between the two— and , or , and not all have lower precedence than their symbolic representations.

Multiple Choice: if . . . else

Consider these two if statements:

if ($password eq $guess) {
  
print "Pass, friend.n";
}
if ($password ne $guess) {
   die "Go away, imposter!n";
}

We know that if the first test condition is true, then the second one will not be—we’re asking exactly opposite questions: Are these the same? Are they not the same?

In which case, it seems wasteful to do two tests. It’d be much nicer to be able to say, “If the strings are the same, do this. Otherwise, do that.” And in fact we can do exactly that, although the keyword is not otherwise but else:

if ($password eq $guess) {
   print "Pass, friend.n";
} else {
   die "Go away, imposter!n";
}

That’s

if ( condition ) { action } else { alternative action }

Like the if statement, those curly braces are required in the else part.

Even More Choices: if . . . elsif . . . else

Some things in life aren’t clear-cut. In some cases, we’ll want to test more than one condition. When looking at several related possibilities, we’ll want to ask questions like “Is this true? If this isn’t, then is that true? If that’s not true, how about the other?” Note that this is distinct from asking three independent questions; whether we ask the second depends on whether or not the first was true. In Perl, we could very easily write something like this:

if ( condition1 ) {
   action1
} else {
   if ( condition2 ) {
      action2
  
} else {
      if ( condition3 ) {
         action3
     
} else {
         action4
     
}
   }
}

You might agree that this looks pretty messy. To make it nicer, we can combine the else and the next if into a single word, elsif . Here’s what the preceding would look like when rephrased in this way:

if ( condition1) {
   action1
} elsif ( condition2 ) {
   action2
} elsif ( condition3 ) {
   action3
} else {
  
action4
}

Much neater! We don’t have an awful cascade of closing curly braces at the end, and it’s easier to see what we’re testing, and when we’re testing it.

Let’s look at an example. Most of us will not go outside if it’s raining, but we’ll always go out for a walk in the snow. We will not go outside if it’s less than 18 degrees Celsius. Otherwise, we’ll probably go out unless we’ve got too much work to do. Do we want to go for a walk?

#!/usr/bin/perl -w
# walking.pl

use strict;

print "What’s the weather like outside? "; chomp(my $weather = <STDIN>);
print "How hot is it, in degrees? ";
chomp(my $temperature = <STDIN>);
print "And how many emails left to reply to? ";
chomp(my $work = <STDIN>);

if ($weather eq "snowing") {
   print "It’s snowing, let’s go!n";
} elsif ($weather eq "raining") {
   print "No way, sorry, it’s raining so I’m staying in.n";
} elsif ($temperature < 18) {
   print "Too cold for me!n";
} elsif ($work > 30) {
   print "Sorry – just too busy.n";
} else {
   print "Well, why not?n";
}

Let’s say it is 201 degrees, we’ve got 27 emails to reply to, and it’s cloudy out there:

$ perl walking.pl
What’s the weather like outside? cloudy
How hot is it, in degrees? 20
And how many emails left to reply to? 27 Well, why not? $

Looks like we can fit a walk in after all.

The point of this rather silly little program is that once it has gathered the information it needs, it runs through a series of tests, each of which could cause it to finish. First, we check to see if it’s snowing:

if ($weather eq "snowing") {
   print "It’s snowing, let’s go!n";

If so, then we print our message and, this is the important part, do no more tests. If not, then we move on to the next test:

} elsif ($weather eq "raining") {
   print "No way, sorry, it’s raining so I’m staying in.n";

Again, if this is true, we stop testing; otherwise, we move on. Finally, if none of the tests are true, we get to the else :

} else {
   print "Well, why not?n";
}

Please remember that this is very different to what would happen if we used four separate if statements. The tests overlap, so it is possible for more than one condition to be true at once. For example, if it was snowing and we had over 30 emails to reply to, we’d get two conflicting answers. elsif tests should be read as “Well, how about if . . . ?”

Another example of using an if/elsif/else is the program we saw earlier, guessnum1.pl . The decision we made in that program was implemented with three if statements:

if ($target == $guess) {
   print "That’s it! You guessed correctly!n";
  
exit;
}
if ($guess > $target) {
  
print "Your number is more than my numbern";
   exit;
}
if ($guess < $target){
  
print "Your number is less than my numbern";
  
exit;
}

Notice that in each if statement we execute the exit() function since, if the condition is true, there is no reason to check any of the following conditions. Instead of using the exit() function in each of the if blocks, this would be better written with an if/elsif/else as shown in guessnum2.pl :

#!/usr/bin/perl -w
# guessnum2.pl

use strict;

my $target = 12;
print "Guess my number!n";
print "Enter your guess: ";
my $guess = <STDIN>;

if ($target == $guess) {
   print "That’s it! You guessed correctly!n";
} elsif ($guess > $target) {
  
print "Your number is more than my numbern";
} elsif ($guess < $target) {
  
print "Your number is less than my numbern";
}

The unless Statement

There’s another way of saying if (not $a) . As always in Perl, there’s more than one way to do it.2 Some people prefer to think “If this is not true, then { … } ,” but other people think “Unless this is true, then { … } . ” Perl caters to both sets of thought patterns, and we could just as easily have written this:

unless ($a) {
  
print "$a is not truen";
}

The psychology is different, but the effect is the same. We’ll see later how Perl provides a few alternatives for these control structures to help them more effectively fit the way you think.

Expression Modifiers

When we’re talking in English, it’s quite normal for us to say

  1. If this is not true, then this happens, or
  2. Unless this is true, this happens.  

Similarly, it’s also quite natural to reverse the two phrases, saying

  1. This happens if this is not true, or 
     
  2. This happens unless this is true.

In Perl-speak, we can take this if statement:

if ($number == 0) {
   die "can’t divide by 0";
}

and rewrite it using expression modifiers as follows:

die "can’t divide by 0" if $number == 0;

Notice how the syntax here is slightly different, it’s action if condition. There is no need for parentheses around the condition, and there are no curly braces around the action. Indeed, the indentation isn’t part of the syntax, so we could even put the whole statement on one line. Only a single statement will be covered by the condition. This form of the if state ment is called an expression modifier.

We can turn unless into an expression modifier too, so, instead of this:

if (not $name) {
   die "$name has a false value" ;
}

you may find it more natural to write this:

die "$name has a false value" unless $name;

Using Short-Circuited Evaluation

There is yet another way to do something if a condition is true. By using the fact that Perl stops processing a logical operator when it knows the answer, we can create a sort of unless conditional:

$name or die "$name has a false value";

How does this work? Well, it’s reliant on the fact that Perl uses short-circuited, or lazy, evalua tion to give a logical operator its value. If we have the statement X or Y , then if X is true, it doesn’t matter what Y is, so Perl doesn’t look at it. If X isn’t true, Perl has to look at Y to see whether or not that’s true. So if $name has a true value, then the die() function will not be executed. Instead, it will do nothing and continue on executing the next statement.

This form of conditional is most often used when checking that something we did succeeded or returned a true value. We will see it often when we’re handling files.

To create a positive if conditional this way, use and instead of or . For example, to add one to a counter if a test is successful, you may say

$success and $counter++;

If you’ll recall, and statements are reliant on both substatements being true. So, if $success is not true, Perl won’t bother evaluating $counter++ and upping its value by 1. If $success was true, then it would.

Looping Constructs

Now we know how to do everything once. What about if we need to repeat an operation or series of operations? Of course, there are constructs available to do this in Perl, too.

In programming, there are various types of loops. Some loop forever, and are called infinite loops, while most, in contrast, are finite loops. We say that a program “gets into” or “enters” a loop, and then “exits” or “falls out” when finished. Infinite loops may not sound very useful, but they certainly can be—particularly because most languages, Perl included, provide you with a way by which you can exit the loop. They will also be useful for situations when you just want the program to continue running until the user stops it manually, the computer powers down, or the heat death of the universe occurs, whichever is sooner.

There’s also a difference between definite loops and indefinite loops. In a definite loop, you know how many times the block will be repeated in advance. An indefinite loop will check a condition in each iteration to determine whether it should do another or not.

There’s also a difference between an indefinite loop that checks before the iteration, and one that checks afterward. The latter will always go through at least one iteration, in order to get to the check, whereas the former checks first and so may not go through any iterations at all.

Perl supports ways of expressing all of these types of loops. First, let’s examine the while loop.

The while Loop

Let’s start with the indefinite loops. These check a condition, then do an action, then go back and check the condition again. The first one is the while loop. As you might be able to work out from the name, this keeps doing something while a condition is true. The syntax of while is much like the syntax of if :

while ( condition ) { action }

Once again, those curly braces are required. Here’s a very simple while loop:

#!/usr/bin/perl – w
# while1.pl

use strict;

my $countdown = 5;

while ($countdown > 0) {
   print "Counting down: $countdownn";
   $countdown–;
}

And here’s what it produces:

$ perl while1.pl
Counting down: 5
Counting down: 4
Counting down: 3
Counting down: 2
Counting down: 1
$

Let’s see a flow chart for this program :

While there’s still a value greater than 0 in the $counter variable, we do these two statements:

print "Counting down: $countdownn" ; $countdown–;

Perl goes through the loop a first time when $countdown is 5—the condition is met, so a message gets printed, and $countdown gets decreased to 4. Then, as the flow chart implies, back we go to the top of the loop. We test again: $countdown is still more than 0, so off we go again. Eventually, $countdown is 1, we print our message, $countdown is decreased, and it’s now 0. This time around, the test fails, and we exit the loop.

while (<STDIN>)

Recall that we talked about using <STDIN> to read from standard input (normally the keyboard). This statement reads the next line of standard input, up to and including the newline character:

$line_in = <STDIN>;

We can put this assignment within a while loop that will read from standard input until end of file (in Unix a ^D, or the Ctrl and D keys pressed at the same time, in Windows a ^Z<enter> ). This loop reads a line at a time into $line_in and then prints the line read in:

while ($line_in = <STDIN>) {
    print $line_in;
}

This behavior, reading from standard input until end of file, is so common that if <STDIN> is by itself within the while loop parens (and only within the while loop parens), then the line of standard input is magically assigned to the special variable $_ . This loop reads each line into $_ , and then the line is printed:

while (<STDIN>) {
    print $_;
}

This is so common that print() defaults to printing $_ :

while (<STDIN>) {
    print;
}

Let’s look at an example of using this magic variable $_ . This program will loop through standard input one line at a time until end of file, and for each line it will print a message followed by the line entered:

#!/usr/bin/perl -w
# while2.pl

use strict;

while (<STDIN>) {
    print "You entered: ";
    print;
}

Following is an example of running this program in Unix:

$ perl while2.pl
hello

You entered: hello
world

You entered: world
good
You entered: good
bye
You entered: bye
^D
$ 

The $_ variable is a useful variable—it is the default argument for many different functions. An example is the chomp() function. The statement

chomp $_;

could have been written as

chomp;

Many Perl programmers find it convenient and readable to write a loop like this one:

while ($line = <STDIN>) {
    chomp $line;
    …
}

using the default nature of $_ :

while (<STDIN>) {
    chomp;
    …
}

Whether or not you write code to take advantage of the magic nature of $_ is a choice for you to make, but we suggest you practice with it enough to be able to read code that others have written where $_ is used.

Infinite Loops

The important but obvious point is that what we’re testing gets changed inside the loop. If our condition is always going to give a true result, we have ourselves an infinite loop. Let’s just remove the second of those two statements:

#!/usr/bin/perl -w
# while3.pl

use strict;

my $countdown = 5;

while ($countdown > 0) {
   print "Counting down: $countdownn";
}

$countdown never changes. It’s always going to be 5, and 5 is, we hope, always going to be more than 0. So this program will keep printing its message until you interrupt it by holding down Ctrl and C. Hopefully, you can see why you need to ensure that what you do in your loop affects your condition.

Should we actually want an infinite loop, there’s a fairly standard way to do it. Just put a true value—typically
1 —as the condition:

while (1) {
  
print "Bored yet?n";
}

The converse, of course, is to say while (0) in the loop’s declaration, but nothing will ever happen because this condition is tested before any of the commands in the loop are executed. A bit silly really.

Looping Until

The opposite of if is unless , and the opposite of while is until . It’s exactly the same as while (not condition ) { … } . Using the condition in the program while1.pl shown previously:

while ($countdown > 0) {

Its logical negation would be

until ($countdown <= 0) {

Therefore, we can write while1.pl as

#!/usr/bin/perl -w
# until.pl

use strict;

my $countdown = 5;

until ($countdown <= 0) {
    print "Counting down: $countdownn";
    $countdown–;
}

And here’s what it produces:

$ perl until.pl
Counting down: 5
Counting down: 4
Counting down: 3
Counting down: 2
Counting down: 1
$

The for Loop

Perl has a for loop, similar to the one found in C/C++/Java. Its syntax is

for (init_expr; test_expr; step_expr) {
    action
}

The init_expr  is done first and once. Then the test_expr  is tested to be true or false. If true, the action is executed, then the step_expr is executed. Then the test_expr is tested to be true or false, etc.

The most common use of a for loop is as an alternative way of writing a while loop that might resemble this one:

$i = 1;
while ($i <= 5) {
   
# do something important
   
$i++;
}

This can be written in a for loop as

for ($i = 1; $i <= 5; $i++) {
   
# do something important
}

The foreach Loop

Perl has another loop called the foreach loop. It is used to loop through lists and arrays. We will talk about arrays in the next chapter, but since we have seen examples of a list, we can look at the foreach loop processing a list of numbers:

#!/usr/bin/perl -w
# foreach.pl

use strict;

my $number;

foreach $number (1 .. 10) {
    print "the number is: $numbern";
}

The foreach loop executes the body of the loop (the print() function in this example) for each number in the list. $number is called the loop control variable, and it takes on the values in the list, one at a time. Recall that (1 .. 10) is shorthand for (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) . This code produces this result:

$ perl foreach.pl
the number is: 1
the number is: 2
the number is: 3
the number is: 4
the number is: 5
the number is: 6
the number is: 7
the number is: 8
the number is: 9
the number is: 10
$

A note about the keywords for and foreach : they are synonyms for each other. In other words, we can say

foreach ($i = 1; $i <= 10; $i++)_ { .. }

and

for $number (1..10) { .. }

foreach is rarely used in place of for , but for is often used instead of foreach . In the spirit of minimal confusion, we will spell out foreach when we have a foreach loop.

We will talk more about foreach in the next chapter when we discuss the array data type.

do .. while and do .. until

When we were categorizing our lists, we divided indefinite loops into those that execute at least once and those that may execute zero times. The while loop we’ve seen so far tests the condition first, and so if the condition isn’t true the first time around, the “body” of the loop never gets executed. There’s two other ways to write our loop to ensure that the body is always executed at least once:

do { action } while ( condition );
do { action } until ( condition );

Now we do the test after the block. This is equivalent to moving the diamond in our flow chart from the top to the bottom.

Here is an example:

#!/usr/bin/perl -w
# dowhiledountil.pl

use strict;

my $i = 1;

print "starting do…while:n";
do {
    print " the value of $i: $in";
    $i++;
} while ($i < 6);

$i = 1;

print "starting do…untiln";
do {
    print " the value of $i: $in";
    $i++;
} until ($i >= 6);

Executing this program produces the following:

$ perl dowhiledountil.pl
starting do…while:
    the value of $i: 1
    the value of $i: 2
    the value of $i: 3
    the value of $i: 4
    the value of $i: 5
starting do…until
    the value of $i: 1
    the value of $i: 2
    the value of $i: 3
    the value of $i: 4
    the value of $i: 5
$

The importance of the do..while and do..until loop is that the body of the loop is always executed at least once.

Expression Modifying

As before, you can use while as a statement modifier. Following the pattern for if , here’s what you’d do with while :

while ( condition ) { statement }

becomes

statement while condition;

Similarly:

until ( condition ) { statement }

becomes

statement until condition;

Therefore, this loop:

while (<STDIN>) {
    print "You entered: $_";
}

can be written as

print "You entered: $_" while <STDIN>;

Loop Control Constructs

Perl provides constructs to allow us to control the flow of our loops. They allow us to break out of a loop, go to the next iteration of the loop, or reexecute the loop. We’ll start with breaking out of a loop.

Breaking Out

The keyword last , in the body of a loop, will make Perl immediately exit, or “break out of” that loop. The remaining statements in the loop are not executed, and you end up right at the end. Here is an example of a program that breaks out of the loop when the user enters the text “done”:

#!/usr/bin/perl -w
# last1.pl

use strict;

while (<STDIN>) {
    if ($_ eq "donen") {
       
last;
    }
    print "You entered: $_";
}

print "All done!n";

$ perl last1.pl
Songs
You entered: Songs
from
You entered: from
the
You entered: the
Wood
You entered: Wood
done
All done!
$

You can use a last in any looping construct ( while , until , for , and foreach ). However, the last does not work with the do {} while or do {} until loops.

Note that last1.pl could have been written using an expression modifier. It can be argued that this code is a bit more readable:

#!/usr/bin/perl -w
# last2.pl

use strict;

while (<STDIN>) {
   
last if $_ eq "donen";
    print "You entered: $_";
}
 

print "All done!n";

Going On to the Next

If you want to skip the rest of the processing of the body, but don’t want to exit the loop, you can use next to immediately go execute the next iteration of the loop by testing the expres sion. Here is an example of a program that reads input from the user, and if the line of input is not blank, the line is printed. It the line is blank, then we immediately go back to read the next line:

#!/usr/bin/perl – w
# next1.pl

use strict;

print "Please enter some text:n";
while (<STDIN>) {
    if ($_ eq "n") {
        next;
    } 
    chomp;
    print "You entered: [$_]n";
}

Here is an example of running this program in Windows:

$ perl next1.pl
Please enter some text:
testing
You entered: [testing]
one
You entered: [one]
two three
You entered: [two three]
^Z<enter>
$

Notice that when the user entered a blank line, then the program immediately read the next line of input.

This program could have be written with an expression modifier:

#!/usr/bin/perl -w
# next2.pl

use strict;

print "Please enter some text:n";
while (<STDIN>) {
   
next if $_ eq "n";
    chomp;
   
print "You entered: [$_]n";
}

Reexecuting the Loop

On rare occasions, you’ll want to go back to the top of the loop, but without testing the condition (in the case of a for or while loop ) or getting the next element in the list (as in a for or while loop). If you feel you need to do this, the keyword to use is redo . This is illustrated in this example:

#!/usr/bin/perl -w
# redo.pl

use strict;

my $number = 10;

while (<STDIN>) {
    chomp;
    print "You entered: $_n";

    if ($_ == $number) {
        $_++;
        redo;
    }
    print "Going to read the next number now…n";
}

If the user enters the value 10, then the input is incremented to 11 and we jump to the beginning of the block, at which point the value will be chomped (which has no effect on the value since it does not end in newline) and then the value 11 is reported. Executing this program in Windows would look like the following:

$ perl redo.pl
5
You entered: 5
Going to read the next number now…
20
You entered: 20
Going to read the next number now…
10
You entered: 10
You entered: 11
Going to read the next number now…
^Z<enter>
$

Loop Labels

By default, last, next , and redo operate on the innermost looping construct only. For instance, in this code:

#!/usr/bin/perl -w
# looplabel1.pl

use strict;

my $i = 1;

while ($i <= 5) {
    my $j = 1;
    while ($j <= 5) {
       
last if $j == 3;
        print "$i ** $j = ", $i ** $j, "n";
        $j++;
   
}
    $i++;
}

the last statement within the innermost loop construct ( while ($j <= 5) ) will last out of the innermost looping construct only. Therefore, each time $j reaches 3 within the inner loop, we last out of the inner loop and increment $i and go back up to test the expression for the outer while loop. This generates this output:

$ perl looplabel1.pl
1 ** 1 = 1
2 ** 1 = 2
2 ** 2 = 4
3 ** 1 = 3
3 ** 2 = 9
4 ** 1 = 4
4 ** 2 = 16
5 ** 1 = 5
5 ** 2 = 25
$

To make the last statement last out of the outer looping construct, we must label the outer looping construct with a loop label. A loop label is a variable that the programmer creates (it is recommended that you use all uppercase names) followed by a colon, preceding the looping construct. This is illustrated in looplabel2.pl :

#!/usr/bin/perl -w
# looplabel2.pl

use strict;

my $i = 1;

OUTER: while ($i <= 5) {
    my $j = 1;
    while ($j <= 5) {
        last OUTER if $j == 3;
        print "$i ** $j = ", $i ** $j, "n";
        $j++;
    }
    $i++;
}

Now, when the last statement is executed, the code jumps out of the outer loop named OUTER :

$ perl looplabel2.pl
1 ** 1 = 1
2 ** 1 = 2
$

goto

As a matter of fact, you can put a label before any statement whatsoever. If you want to really mess around with the structure of your programs, you can use goto LABEL to jump anywhere in your program. Whatever you do, don’t do this. This is not to be used. Don’t go that way.

We’re telling you about it for the simple reason that if you see it in anyone else’s Perl, you can laugh heartily at them. goto with a label is to be avoided like the plague.

Why? Because not only does it turn the clock back 30 years (the structured programming movement started with the publication of a paper called “Use of goto considered harmful”), but it tends to make your programs amazingly hard to follow. The flow of control can shoot off in any direction at any time, into any part of the file, maybe into a different file. You can even find yourself jumping into the middle of loops, which really doesn’t bear thinking about. Don’t use it unless you really, really, really understand why you shouldn’t. And even then, don’t use it. Larry Wall has never used goto with a label in Perl, and he created Perl.

Don’t. (He’s watching.—Ed)

Summary

Before this chapter, our programs plodded along in a straight line, following one statement with another.

We’ve now seen how we can react to different circumstances in our programs, which is the start of flexible and powerful programming. We can test whether something is true or false using if and unless , and take appropriate action. We’ve also examined how to test multiple related conditions using elsif .

We can repeat areas of a program, in several different ways, using while , until , for and foreach .

Finally, we’ve examined some ways to alter the flow of Perl’s execution through these loops. We can break out of a loop with last , skip to the next element with next , and start processing the current element again with redo .

Exercises

  1. Modify the number guessing program guessnum2.pl so that it loops until the correct answer is entered.
  2. Write a program that prints the squares of the numbers between 1 and 10. 
     
  3. Write a program to print all the numbers between 1 and 50 that are evenly divisible by 5. Loop by 1, not by 5!

1. Celsius, that is.

2. TMTOWTDI–our favorite acronym!

 

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye