12.4.1 Writing Mapping Functions
12.4.1 Writing Mapping Functions
In this section, we write a few more simple functions that map a function to a sequence of parameters. In particular, we write four mapping functions: mapAtoB, map0toN, map1toN and mapF. We start with the code for mapAtoB and use its definition to write map0toN and map1toN. We then write the code for the more general mapF function. Finally, we write code for a function called newmapAtoB which is a version of mapAtoB written using the mapF function.
Before proceeding with writing these functions, let us first write a function called funcallS which takes a reference to a function and calls this function on a scalar parameter. It returns a scalar result.
Program 12.9
sub funcallS {
my ($fn, $arg) = @_;
&$fn($arg);
}
Next, we write the function mapAtoB that takes as parameters a reference to a function, a low numeric value, a high numeric value, and optionally a numeric step. If the step is not provided, it defaults to 1. mapAtoB takes the referenced function and applies it to a sequence of numbers starting with the low value. It increments the number on which the function is applied by step in each iteration. When the number becomes bigger than the high value, it stops the iteration. If the high value is smaller than low value to start with, the function is not applied even once. mapAtoB returns the results of applying the function to the sequence of numbers in a list. The definition for mapAtoB is given
below.
Program 12.10
sub mapAtoB {
my ($fn, $A, $B, $step) = @_;
my ($i, @result);
unless (defined ($step)) { $step = 1;};
for ($i = $A; $i <= $B; $i = $i + $step){
push (@result, funcallS ($fn, $i));
}
return @result;
}
In the definition of mapAtoB we check to see if a value has been provided from $step in the call. This check is done using the defined function. If a value has not been provided, $step is initialized to 1. In the for loop, we apply the function referenced by $fn to the current value of the loop index variable $i in each iteration. This application is done by calling the funcallS function defined a little earlier.
Now, we define the add1 function that we map on the desired sequence of numbers and then make two calls to mapAtoB that return the results shown.
sub add1 {
$_[0] + 1;
}
The call
mapAtoB (\&add1, 20, 40, 2)
returns
21 23 25 27 29 31 33 35 37 39 41
and the call
mapAtoB (\&add1, 20.1, 40, 2.5)
returns
21.1 23.6 26.1 28.6 31.1 33.6 36.1 38.6
Once again, note that we obtain the reference to a named function such as add1 by preceding it with &.
Having defined the function mapAtoB, we can define the two functions map0toN and map1toN in terms of it. map0toN applies a function to every integer from 0 through N, and map1toN does the same for integers 1 through N.
Program 12.11
sub map0toN{
($f, $N) = @_;
mapAtoB ($f, 0, $N);
}
sub map1toN{
($f, $N) = @_;
mapAtoB ($f, 1, $N);
}
#prints 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
print join " ", map0toN (\&add1, 20), "\n";
#prints 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
print join " ", map1toN (\&add1, 20), "\n";
We now define the function mapF that is more general than the three mapping functions we have written so far. mapF takes four parameters: a reference to the function to be mapped, a starting value for the mapping sequence, a reference to a function that tests to see if the end of the desired sequence has been reached, and a reference to a function that obtains a succeeding number from the current one.
Program 12.12
sub mapF {
my ($f, $start, $testF, $succF) = @_;
my ($i, @result);
for ($i = $start; !funcallS ($testF, $i); $i = funcallS ($succF, $i)){
push @result, funcallS ($f, $i);
}
return @result;
}
We see that the new value of the index variable $i is obtained by applying $succF on the current value of $i. This is achieved by the expression
$i = funcallS ($succF, $i)
The operating condition for the loop is given as
!funcallS ($testF, $i);
That is, as long as the application of the test function on the index variable returns a false value, the iteration continues. In other words, when an application of the test function $testF on $i returns true, the iteration stops. For example, our test function could see if the number under consideration is a prime number and stop iteration if it is so. A definition for such a test function is given below.
Program 12.13
sub isPrimeP {
my $number = $_[0];
my $i;
for ($i= 2; $i <= sqrt ($number); $i++){
if ($number % $i == 0 ) {
return 0;}
}
return 1;
}
Now, we can make the following calls to mapF obtain the results given.
#prints 21 22 23
print join " ", mapF (\&add1, 20, \&isPrimeP, \&add1), "\n";
#prints 201 202 203 204 205 206 207 208 209 210 211
print join " ", mapF (\&add1, 200, \&isPrimeP, \&add1), "\n";
#prints 2031 2032 2033 2034 2035 2036 2037 2038 2039
print join " ", mapF (\&add1, 2030, \&isPrimeP, \&add1), "\n";
We can now write a couple more functions that print prime numbers between any two integers. The definitions of the two functions are given below.
Program 12.14
sub printIfPrime{
if (isPrimeP ($_[0])) {print " $_[0] ";}
else {}
}
sub printPrimes {
my ($low, $high) = @_;
mapAtoB (\&printIfPrime, $low, $high);
}
Following these two definitions, we can make the following call
printPrimes (100000, 100999);
and obtain the following primes between 100,000and 100,999.
100129 100151 100153 100169 100183 100189 100193 100207 100213
100237 100267 100271 100279 100291 100297 100313 100333 100343
100357 100361 100363 100379 100391 100393 100403 100411 100417
100447 100459 100469 100483 100493 100501 100511 100517 100519
100523 100537 100547 100549 100559 100591 100609 100613 100621
100649 100669 100673 100693 100699 100703 100733 100741 100747
100769 100787 100799 100801 100811 100823 100829 100847 100853
100907 100913 100927 100931 100937 100943 100957 100981 100987
100999
Please note that the numbers do not come out formatted as we show them above.
Finally, we close this section by rewriting the mapAtoB function in terms of the mapF function. This new definition is given below.
Program 12.15
sub newmapAtoB {
my ($fn, $A, $B, $step) = @_;
unless (defined ($step)) { $step = 1;};
mapF ($fn, $A,
sub { my $x = $_[0];
$x > $B;
},
sub { my $x = $_[0];
$x + $step;
}
);
}
In this definition, we call mapF with four parameters as required. There is nothing unusual about the first two parameters. However, the third and the fourth parameters are unusual in the sense that they are in-line references to functions. We define the two functional parameters inside the call to mapF without any names. These two are anonymous or nameless functions. To reiterate, we write an anonymous function in Perl by writing the keyword sub and following it by a block that contains the definition of the function. Since these are functions, they return the values computed by the last statements inside them.
