Question

Suppose I have the following two equal-sized arrays in my perl program:

my @arr1 = 1..5;
my @arr2 = 6..10;

I'm trying to get their dot-product using the reduce function defined in the List::Util core module but the following is not working for me:

my $dot_prod = reduce { $arr1[$a] * $arr2[$a] + $arr1[$b] * $arr2[$b] }0..$#arr1;

I get 50 as my output instead of the expected 130.

Was it helpful?

Solution

The documentation describes the behavior of reduce as follows:

The first call will be with $a and $b set to the first two elements of the list, subsequent calls will be done by setting $a to the result of the previous call and $b to the next element in the list.

Thus, in this case, on the first iteration reduce will set $a = 0 and $b = 1, and hence, execute

$arr1[0] * $arr2[0] + $arr1[1] * $arr2[1] 

This temporary result happens to be 20.

Now, for the second iteration, $a is set to the result of the previous iteratrion and so $a = 20 and $b = 2. Thus, the following will be executed

$arr1[20] * $arr2[20] + $arr1[2] * $arr2[2]

which is not what we want.

A possible workaround:

prepend an initial 0 to the list provided as input to reduce as follows:

my $dot_prod = reduce { $a + $arr1[$b] * $arr2[$b] } 0, 0..$#arr1;       

This gives us the desired result since on the first iteration $a = $b = 0 and we'll compute

0 + $arr[0] * $arr[0]

whose result will be 6.

Then in the second iteration, we'll have $a = 6 $b = 1 and so we'll compute

6 + $arr1[1] * $arr2[1]

etc.

OTHER TIPS

my @arr1 = 1..3;
my @arr2 = 6..8;

use List::Util qw(reduce sum) ;

my $dot_prod0 = reduce { $a + $arr1[$b] * $arr2[$b] } 0,0..$#arr1;       #reduce

print "Dot product0 = ".$dot_prod0."\n";
my $dot_prod1 = sum map  { $arr1[$_]*$arr2[$_] } 0..$#arr1;              #sum map
print "Dot product1 = ".$dot_prod1."\n";
my $dot_prod2 = reduce { $a+$b } map { $arr1[$_]*$arr2[$_] } 0..$#arr1;  #reduce map
print "Dot product2 = ".$dot_prod2."\n";

Honestly,

my $dot_prod = reduce { $a + $arr1[$b] * $arr2[$b] } 0, 0..$#arr1; 

isn't the most readable. There is this:

my $dot_prod = sum map { $arr1[$_]*$arr2[$_] } 0..$#arr1;

But that doesn't use reduce. Well, we could simply implement sum in terms of reduce instead of using List::Util's, and perhaps even inline it:

my $dot_prod = reduce { $a+$b } map { $arr1[$_]*$arr2[$_] } 0..$#arr1;

None of those three solutions are purely functional, though.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top