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.