2.5.2 Definite Iteration

In definite iteration, the number of iterations is determined in advance. We may not always have the number of iterations on our finger tips, but if we want to compute the exact number, we can easily do so. Definite iteration also uses of a loop control variable which is connected to the loop in a fashion much more tightly than indefinite iteration. The loop body is executed with the control variable taking each of a pre-determined sequence of values. The values may be arithmetic values that are incremented or decremented in each step. They may also be the individual values from a pre-determined sequence such as a list, or a set such as all the keys of a hash. The sequence of values that the loop control variable takes is sometimes called the control sequence. Perl has two such constructs: for and foreach.
2.5.2.1 The for Statement
The for loop or a cousin of it appears in all high level programming languages. The for loop is controlled by a loop control variable. The variable is given an initial value. How the value of this variable is updated after every iteration is explicitly given. In addition, a conditional expression is provided to determine if the iterations should go on or halt. Perl does not require us to specify any of these three, but they normally are given explicitly. If any one of them is not specified, the programmer must assume complete responsibility to ensure that the loop does not continue indefinitely unless there an explicit and rare need for infinite loop execution.
The general syntax of the for loop is given below.
for ( initialization ; termination-condition ; update ) block
The for keyword is necessary. The initialization of the loop control variable, the termination condition, and the manner in which it is updated after every iteration is provided within parentheses following the keyword for. The block of statements is executed as many times as dictated by the loop control variable’s changing value through the life of the loop. The termination condition must essentially test the value of the loop control variable in some fashion. It does not have to be a simple equality or inequality test, but may be fairly complicated involving more complex expressions and function calls. The loop control variable does not have to an arithmetic scalar.
The following program is repeated here from Section 1.4.2
Program 2.22

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

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

use strict 'vars';
my $fahrenheit;
my $celsius;

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

In this program, the loop control variable is $fahrenheit. Its initial value is 0 and is updated by adding 20 after each iteration. The loop continues as long as $fahrenheit’s value is 300 or less. The output of the program can be seen in Section 1.4.
Different programming languages treat the loop control variable in different manners as to its status. In Perl, it is an ordinary variable that must be declared a-priori if we specify use strict or a variation of it in the current block. In a language like Ada, the use of the for statement itself constitutes a declaration of the loop control variable. Thus, in Perl, the program given above could have been written in the following slightly different manner.
Program 2.23

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

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

use strict 'vars';
my $celsius;

#loop through the Fahrenheit values
for (my $fahrenheit = 0; $fahrenheit <= 300; $fahrenheit = $fahrenheit+20){
$celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
}

If we look at the expression after the for keyword, we see that the $fahrenheit variable is declared in the expression with my and given an initial value.
A loop written using for can be easily converted to a loop using while whereas the reverse many not that easy. In fact, the programs given in Section 2.5.1 and this section are versions of the same program, one written using while and the other written using the for loop although we did not use the continue keyword earlier. The following program shows that the continue block, if used, should be used to specify the updates to the loop control variables. A continue block is not found in most other programming languages. Its semantics is such that it is executed after the loop’s body is evaluated, before the conditional is tested again. With the continue keyword and its associated block, we can rewrite the program given immediately above as the following.
Program 2.24

#!/usr/bin/perl
#file while11.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;
}
continue{
$fahrenheit = $fahrenheit + $step;
}

The continue block updates the value of the loop control variable $fahrenheit. It is executed after the body of the block is executed and before control moves to the top of the block for possible another iteration. The use of a continue block allows one to write an exact equivalent of a for loop in terms of while without being kludgy.
The initialization and update expressions, and the conditional expression inside the parentheses in a for loop are optional. In such a case, we must provide the appropriate loop exit statement to finish the loop. The following program shows a use of for without anything inside the parentheses that immediately follow the keyword for.
Program 2.25

#!/usr/bin/perl
#file forEmpty.pl
#print Fahrenheit-Celsius table for 0, 20, ..., 300 degrees Fahrenheit

use strict 'vars';
my $fahrenheit;
my $celsius;

#loop through the Fahrenheit values
#for ($fahrenheit = 0; $fahrenheit <= 300; $fahrenheit = $fahrenheit+20){
$fahrenheit = 0;
for (;;){
$celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
$fahrenheit = $fahrenheit + 20;
if ($fahrenheit > 300){last;}
}

Here, the for loop is exited explicitly using last when the termination condition is satisfied. This is like the while loop when 1 is used as a conditional.
2.5.2.2 The foreach Statement
The foreach statement goes over each element of a list of elements and executes the body of the loop once for every element. The program given above can be rewritten using a foreach loop assuming that the increments in temperature are in steps of one.
Program 2.26

#!/usr/bin/perl
#file foreach1.pl
#print Fahrenheit-Celsius table for 0, 20, ..., 300 degrees Fahrenheit

use strict 'vars';
my $fahrenheit;
my $celsius;

#loop through the Fahrenheit values
$fahrenheit = 0;
foreach $fahrenheit(1..10){
$celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
}

Here, we use the range operator 1..10 to list the values that the for loop iterates over. 1..10 actually creates the list (1, 2, 3, 4, 5, 6, 7, 8, 9, 10). We see lists in detail in Chapter 3. The foreach loop goes over this list’s items sequentially one by one and executes the body of the loop. A foreach loop can also have a continue block although it is difficult to envision much use for it.
If the increments are not in steps of one, but in steps of an arbitrary value, as in the previous programs, we will have to pre-compute the list of values to iterate over. This may not be the best use of the foreach statement. We are better off using for or a while loop in such a situation. We show how it can be done using map in Section 2.8. Note that the list from which individual elements are used to iterate over can be any list such as the following.

(1, 10, 11, 25, "abc")

It can be even a mix of numbers, strings, references as long as we know how to handle each entry inside the body of the loop. The output of the program is given below.

1 -17.2
2 -16.7
3 -16.1
4 -15.6
5 -15.0
6 -14.4
7 -13.9
8 -13.3
9 -12.8
10 -12.2

The foreach statement is used quite frequently with hashes to go over all elements. The following example has been discussed earlier in Section 1.9. Here, we illustrate how foreach can be used with a hash. Hashes are discussed in detail in Chapter 3. A hash is multi-component data structure like a list, but where elements are addressed or indexed using not positional integers such as 0 or 10, but using strings.
Program 2.27

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

use strict;
my (%hometowns, %heights, %weights, %ages, $friend);

%hometowns = ('Tommy' => 'Washington', 'Chad' =>'San Francisco',
'Jeff' => 'Boulder',
'Aaron' => 'Golden', 'Rick' => 'Montreal', 'Sean' => 'Montreal',
'Todd' => 'Colo Springs');
%heights = ('Tommy', 66, 'Aaron', 68, 'Rick', 67, "Chad", 73, 'Jeff', 70,
'Sean', 67, "Todd", 73);
%weights = ("Tommy" => 140, "Sean" => 140, "Todd" => 190, "Chad" => 180,
"Jeff" => 145, 'Aaron' => 155, "Rick" => 135, );
%ages = ('Aaron', 23, "Rick", 21, 'Tommy', 18, "Sean", 21, 'Chad', 23,
'Jeff', 21);

$weights {Aaron} = 157;
$ages {"Todd"} = 22;
$hometowns {'Tommy'} = 'Washington DC';

printf "%5s %15s %5s %5s %3s\n", "Name", "Hometown", "Height",
"Weight", "Age";
print "-" x 45, "\n";

foreach $friend (keys (%hometowns)){
printf "%5s %15s %3d %3d %2d\n", $friend, $hometowns{$friend},
$heights{$friend}, $weights{$friend}, $ages{$friend};
}

Here, there are four hashes, %hometowns, %heights, %weights and %ages. Values can be assigned to a hash by giving a list of pairs. The index and the value of a pair can be separated either by => or just a comma. Thus, as we see in the assignment to %hometowns, that we use => to separate the index and key of a pair, and comma to separate individual pairs from one another. In the assignment to %ages and %heights, the elements are all separated by , and thus look like a regular list although the elements are paired up as key and value. Individual elements of a hash can also be assigned. In

$weights {Aaron} = 157;

we are assigning a value to the element that corresponds to the key Aaron in the hash %weights.
The foreach statement occurs at the end of the program. Its structure is

foreach $friend (keys (%hometowns)){...}

where the dots are replaced by the block’s content. This foreach statement loops over every element of the list given below.

keys (%hometowns)

keys is a built-in function that gives all the keys of a hash. In this case, the list of keys is the following.

('Tommy', 'Chad', 'Jeff'', 'Aaron', 'Rick', 'Sean', 'Todd');

The keys function can return the keys in an arbitrary order. The foreach loop goes over each element of this list of keys. When it picks an element from this list, it temporarily calls it $friend. Inside the loop, the value stored in the various hashes corresponding to the keyword $friend are printed. The output of this program is given in Section 1.9.