2.8 Mapping
The map function is discussed in more detail in Chapter 12. However, it is introduced here because it is definitely a very useful and powerful program control structure.
We often execute a block of statements for every element of a list. This is exactly what foreach is meant to do. foreach is a loop statement. There is a built-in function called map that performs the exact same objective in a slightly different manner. Mapping is very commonly used in a functional programming language such as Lisp. In Perl, map is not so commonly used although sometimes it can be used to produce elegant pieces of code. Thus, map is a functional equivalent of foreach. map receives its name from its origin in a purely functional programming language where it is used to map a function onto every element of a list. Perl is a hybrid language that provides strong functional programming abilities. Functional programming is discussed at length in Chapter 12.
The following program, once again, converts Fahrenheit temperatures to Centigrade. It does so by mapping a function onto the pre-computed list of Fahrenheit temperature values. Because of this need for prior computation of the list of values, it may not always be convenient.
Program 2.29
#!/usr/bin/perl
#file fahrenheitmap.pl
use strict;
my ($lower, $upper, $step, @values);
#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
#obtain the list of Fahrenheit temperatures to convert; may potentially be big
@values = map {$_ * $step} (0..$upper/$step);
map {convertM ($_)} @values;
#perform the conversion on the list of Fahrenheit values
####################################
####subroutine to convert a list of Fahrenheit temperatures
sub convertM{
my ($fahrenheit) = @_;
my $celsius = 5/9 * ($fahrenheit - 32);
printf "%4.0f %6.1f\n", $fahrenheit, $celsius;
}
The program computes the list of Fahrenheit temperatures and stores them in the list @values. @values is computed in the following manner.
@values = map {$_ * $step} (0..$upper/$step);
This statement shows the essential characteristics of map. In Perl, map takes a block of statements that is executed for every element of a list. In this case, this list is obtained using the expression
0..$upper/$step
that uses the range operator. The range operator, as used here, produces a list of values in incremental sequence where the increment is 1. It produces the integer sequence given below.
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
The map function iterates over every element of the list given to it. It takes an element one by one, calls it the special variable $_ and executes the block of statements on it. The block is
{$_ * $step}
and therefore, map produces the list with the following elements.
0, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300
Next, there is another call to map.
map {convertM ($_)} @values;
This map statement calls the convertM function or subroutine on every element of the list of Fahrenheit temperatures. This concludes the main program although the subroutine is still to be discussed.
Subroutines are a very important part of any programming language. A special type of subroutine is called a function. Subroutines are discussed at great length in Chapter 12. A subroutine has a name and an associated block called the body. A subroutine in Perl gets a list of arguments. This list of arguments is always known by the special variable @_. In the subroutine convertM, the argument list has only one value, a Fahrenheit temperature. The statement
my ($fahrenheit) = @_;
takes the only element in the list of arguments and calls it $fahrenheit. The my declaration ensures that this variable $fahrenheit is local to the body of the subroutine and not available outside the subroutine. The subroutine converts the Fahrenheit temperature to Centigrade and prints it. This is the subroutine that is mapped onto the list of Fahrenheit temperatures in the main program.
