2.5.1 Indefinite Iteration

There are two indefinite iteration statements in Perl. They are while and until.
2.5.1.1 The while Statement
The general syntax of the while statement is given below.
while ( conditional ) block
while is a reserved keyword that a programmer is not allowed to use. The conditional evaluates to true or false. The following program is repeated from Section 1.4.1 where it is first presented.
Program 2.16

#!/usr/bin/perl
#file fahrenheit.pl

use strict vars;
my ($lower, $upper, $step, $fahrenheit, $celsius);

#print Fahrenheit-Celsius table for 0, 20, ..., 300 degrees Fahrenheit

$lower = 0; #lower limit of temperature table
$upper = 300; #upper limit
$step = 20; #step size

$fahrenheit = $lower;
#loop through the Fahrenheit values
while ($fahrenheit <= $upper){
$celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
$fahrenheit = $fahrenheit + $step;
}

In this program, three variables, $lower, $upper and $step, are given numeric values. Then, a variable $fahrenheit is assigned the initial value of $lower. $fahrenheit is the variable that determines the number of iterations of the loop. Its value changes during the iteration of the loop and the current value determines if the loop’s body or the block of statements is executed another time. In this particular case, the conditional is given as follows.

($fahrenheit <= $upper)

$upper has a fixed value of 300 during the course of the program. The initial value of $fahrenheit is 0 before the while statement is executed.
There is a block of statements associated with the while keyword. This block of statements is executed as long as the conditional is satisfied. In the first iteration, the conditional is evaluated as 0 <= 300. This conditional returns true. Therefore, the body of the while is executed at least once. Note that if the conditional is not satisfied initially, the body of the while loop is not executed even once. In such a case, the program control flows to the statement, if any, after the while loop. Otherwise, the program terminates. Inside the block that is the body of the loop, the value of $fahrenheit is converted to Celsius degrees and the two temperatures printed. The last statement inside the block changes the current value of the determining variable $fahrenheit by adding $step to it.

$fahrenheit = $fahrenheit + $step;

Thus, at the end of the block at the end of the first iteration, the value of $fahrenheit is updated and becomes 20.
In a while loop, the program control always moves to the top of the loop after the block of statements is executed once. The conditional of the while loop is evaluated again with the updated values of the variable. In this particular case, the conditional is evaluated as 20 <= 300. This still returns true. Thus, the block is executed a second time. In this fashion, repeated evaluation of the loop continues with the responsible loop variable updated at the end of each iteration. Note that there may be several variables whose values are updated in each iteration. The values do not need to be necessarily updated at the end of each iteration, but anywhere inside the block is acceptable. In this specific case, the iteration continues till the value of $fahrenheit becomes 300. This is the last iteration of the loop. At the end of this iteration, the value of $fahrenheit becomes 320 and the conditional is evaluated as 320 <= 300. This returns false and the control of the program moves to the statement, if any, below the while loop. If there is no such statement, the program ends.
A very important point must be noted when we write while loops. We must usually choose one or more variables as loop control variables. These variables must be given initial values. The conditional of the while loop must be written carefully noting that the loop may not be executed even once if the conditional is not satisfied at the outset. Inside the loop’s body, the values of the loop control variables must be updated. If the values are not updated, it is quite likely that the execution of the loop will continue for ever. This is usually an error condition that is called the infinite loop error. The problem occurs frequently not only with beginning programmers, but with experienced programmers as well. Everyone writes an infinite loop once in a while! Thus, when writing a loop, whether it is a while loop or anything else, utmost caution should be exercised to ensure that the loop’s execution actually terminates.
Sometimes it is necessary to write a while loop that has a conditional that always evaluates to true. In such situations, usually the conditional is given simply as the number 1. The following program is a variation of the previous program that uses this second way of writing a while loop.
Program 2.17

#!/usr/bin/perl
#file while1-1.pl

use strict vars;
my ($lower, $upper, $step, $fahrenheit, $celsius);

#print Fahrenheit-Celsius table for 0, 20, ..., 300 degrees Fahrenheit
$lower = 0; #lower limit of temperature table
$upper = 300; #upper limit
$step = 20; #step size

$fahrenheit = $lower;
#loop through the Fahrenheit values
while (1){
$celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
$fahrenheit = $fahrenheit + $step;
if ($fahrenheit > $upper){last;}
}

In this case, it is not really necessary and it may not be a good idea to write the program in this fashion. We do it to illustrate that the two versions of the program are semantically the same. A while loop written in this fashion must be explicitly exited. The exiting in this program is done by the following statement.

if ($fahrenheit > $upper){last;}

The last statement used inside the loop’s body moves the control to after the last statement in the block. The control is not sent up to the top of the loop for another evaluation of the conditional and possible repetition of the loop body’s execution. This effectively means that the execution of the loop terminates. Thus, last is an operator used to alter the normal flow of control in a loop. The normal flow of control in the current loop dictates the program control to move to the top of the block after it is executed once, and do this for ever. last changes the location where flow of control moves if the conditional is satisfied. last works with loop statements such as while. Here, the loop does not have a name or a label. In such a case, last takes control to the statement immediately following the } that closes the block’s statements. If there are several blocks enclosed one within the other, last exits the innermost block in which it occurs if none of the blocks is labeled. If any of the blocks has an explicit name or label, last can be given the label of the block to exit. If last is given a block’s label, it can exit blocks which are not necessarily the innermost. Thus, to exit out of a non-innermost enclosing block, labels on loops are essential. Some looping blocks have what is called a continue block. The last statement prohibits the program from executing the continue block associated with the looping block being exited or broken out of.
The following is another simple program that prints the current time on the screen. It does so continuously till the program is killed by typing an interrupt signal such as Control-C in Unix, or the window in which the program is running is killed.
Program 2.18

#!/usr/bin/perl
#while1.pl
use strict;
my $time;

while (1){
$time = localtime ();
print "$time\n";
sleep 1;
}

Once again, one needs to be careful in that a program such as this one is in an infinite loop and thus, may eat up system resources that can be used elsewhere. In this program, we make the program sleep for one second before printing the time again. localtime is a built-in Perl function that gives the current local time. The output of this program looks like the following, but continues for ever.

Thu Jun 7 03:50:28 2001
Thu Jun 7 03:50:29 2001
Thu Jun 7 03:50:30 2001
Thu Jun 7 03:50:31 2001
Thu Jun 7 03:50:32 2001
Thu Jun 7 03:50:33 2001
Thu Jun 7 03:50:34 2001
Thu Jun 7 03:50:35 2001
Thu Jun 7 03:50:36 2001
Thu Jun 7 03:50:37 2001
Thu Jun 7 03:50:38 2001
Thu Jun 7 03:50:39 2001
Thu Jun 7 03:50:40 2001

Notice that the time printed by the program may not move ahead by an exact second from one iteration to another. This may be for various reasons such as the amount of time taken by the rest of the program, and what else is running on the machine, and whether it is a machine with one user or multiple users.
Before we finish our discussion of while, we discuss a common use of the while loop which is to read lines from a file or a set of files. The following program repeated from Section 1.6.1 show such use.
Program 2.19

#!/usr/bin/perl
#file name: read-file.pl
use strict;
my ($filename, $linecount);

print "Please give me a file name >> ";
$filename = ;
chomp ($filename);

$linecount = 1;
print "Printing file $filename\n\n";

open (INFILE, $filename);
while (){
print "$linecount\t$_";
$linecount++;
}

The program asks for a file name from the user. It then opens the file using the filehandle INFILE. The conditional of the while loop that follows is given as

()

that uses the angle operator < > to read a line from a file. The special variable $_ is assigned the line read. $_ is the variable implicitly used in this and many other situations in Perl. Inside the body of the while loop, the line just read is printed. A line number is printed in the front of the line. An interaction with this program is given in Section 1.6.1.
The crucial point to note in this case is that when the file is finished reading, the < > operator returns undef as the value. undef evaluates to false in a scalar context. Thus, the while loop is exited after a file is read completely.
2.5.1.2 The until Statement
The until statement is similar to the while statement except that the meaning of the conditional is reversed. The conditional is still evaluated before the loop is executed. The following is a rewrite of the temperature conversion program given earlier in Section 2.5.1.1. It uses an until statement instead of while.
Program 2.20

#!/usr/bin/perl
#file until1.pl

use strict vars;
my ($lower, $upper, $step, $fahrenheit, $celsius);

#print Fahrenheit-Celsius table for 0, 20, ..., 300 degrees Fahrenheit

$lower = 0; #lower limit of temperature table
$upper = 300; #upper limit
$step = 20; #step size

$fahrenheit = $lower;
#loop through the Fahrenheit values
until ($fahrenheit > $upper){
$celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
$fahrenheit = $fahrenheit + $step;
}

Notice that the conditional of the while loop in Section 2.5.1.1 is

($fahrenheit <=upper)

whereas the conditional of the until is

($fahrenheit > $upper).

Although the conditional specifies a termination condition, it is evaluated before the loop is executed. Thus, if the conditional evaluates to false to begin with, the loop is not executed even once.
2.5.1.3 Simulating A while Loop with a Bare Block
We can simulate how a while loop works with a bare block if we know how to alter the control flows in a loop. In a loop such as while or until, the conditional is evaluated and based on its value, the loop is either executed or control falls to the statement following the loop. However, this normal course of flow of control can be altered using the so-called loop control or jump operators: last, next and redo. We have seen how last works already. We see the use of redo in addition to last in the example that follows. The block used here also has a label although it is not necessary. In Perl, a bare block is considered a loop that is executed just once, and thus, can be labeled. Although a bare block is normally executed only once, we execute it like a while loop below.
In this program, the bare block is given a label or name called CONVERT. A label must be followed by a colon. The first statement inside the block tests to see if the loop should be executed one more time or exited. If $fahrenheit, the loop control variable, crosses the upper limit given by $upper, the loop is exited.

last CONVERT;

takes control to just after the closing brace of the loop, thus killing the loop’s activities. Following this loop control statement, the program converts the current Fahrenheit temperature to Celsius, prints the results and updates it by adding $step to it. The last statement of the loop tests the updated value of $fahrenheit to see if the loop needs to be executed again. In this case, if $fahrenheit is less than or equal to $upper, the program is instructed to execute the loop named CONVERT again. The redo statement takes control to the top of the loop and re-executes it without evaluating the conditional of the loop, if any. In this case, there is no conditional, and thus redo causes another iteration of the loop. This continues till the iterations are stopped by the exit from the loop, caused by last. The redo command prohibits the program from executing the corresponding continue block if any.
The last loop control command is next. next stops executing the current iteration of the loop wherever it is, executes the corresponding continue block if any. It then evaluates the conditional to decide if the loop should be executed again or not.
Program 2.21

#!/usr/bin/perl
#file while10.pl

use strict vars;
my ($lower, $upper, $step, $fahrenheit, $celsius);

#print Fahrenheit-Celsius table for 0, 20, ..., 300 degrees Fahrenheit

$lower = 0; #lower limit of temperature table
$upper = 300; #upper limit
$step = 20; #step size

$fahrenheit = $lower;
#loop through the Fahrenheit values

CONVERT:
{ if ($fahrenheit > $upper){last CONVERT;}
$celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
$fahrenheit = $fahrenheit + $step;
if ($fahrenheit <= $upper){redo CONVERT;}
}