Dmytro Leonenko
Dmytro Leonenko

Reputation: 1501

How can I sum arrays element-wise in Perl?

I have two arrays:

@arr1 = ( 1, 0, 0, 0, 1 );
@arr2 = ( 1, 1, 0, 1, 1 );

I want to sum items of both arrays to get new one like

( 2, 1, 0, 1, 2 );

Can I do it without looping through arrays?

Upvotes: 14

Views: 19224

Answers (9)

mwp
mwp

Reputation: 8467

I'm not sure what you plan to do with the sum once you have it, but you plan to do more vector-y type stuff, then Math::Matrix might be a good fit.

use Math::Matrix;

my $foo = Math::Matrix->new([ 1, 0, 0, 0, 1 ]);
my $bar = Math::Matrix->new([ 1, 1, 0, 1, 1 ]);
my $sum = $foo->add($bar);

Upvotes: 0

From http://www.perlmonks.org/?node_id=122393

@a = qw(1 2 3 4);
@b = qw(1 2 3 4);
@c = (); 

@c = map { $a[$_] + $b[$_] } ( 0 .. (@a > @b ? $#a : $#b) );

Or:

$c[@c] = $a[@c] + $b[@c] while defined $a[@c] or defined $b[@c];

Or:

$c[$_] = $a[$_] + $b[$_] for 0 .. (@a > @b ? $#a : $#b);

Or (in Perl 6):

@c = @a ^+ @b

Upvotes: 2

asjo
asjo

Reputation: 3194

To avoid (explicit) looping, here is a solution that uses recursion "instead":

#!/usr/bin/perl

use v5.20;

my @arr1 = ( 1, 0, 0, 0, 1 );
my @arr2 = ( 1, 1, 0, 1, 1 );

my @result=non_looping_pairwise_sum([ @arr1 ], [ @arr2 ]); # pass in copies, so the originals are not modified
say "@result";

sub non_looping_pairwise_sum { # only handles lists that have the same length
    my ($a1, $a2)=@_;

    return () if (scalar(@$a1)==0 and scalar(@$a2)==0);

    my $e1=shift @$a1;
    my $e2=shift @$a2;

    return ($e1+$e2, non_looping_pairwise_sum($a1, $a2));
}

Output:

2 1 0 1 2

Note that use v5.20 means you don't have to write use strict; use warnings, I think.

Apologies/kudos to @parm for the idea.

Upvotes: 0

ghostdog74
ghostdog74

Reputation: 342263

what's wrong with looping over arrays? that's the fundamentals.

@arr1 = ( 1, 0, 0, 0, 1 );
@arr2 = ( 1, 1, 0, 1, 1 );
for ($i=0;$i<scalar @arr1;$i++){
    print $arr[$i] + $arr2[$i] ."\n";
}

Upvotes: 7

parm
parm

Reputation: 11

If you're really afraid of looping, then you can binary chop the arrays, sum the pairs, then recursively reassemble the resulting array. No looping there, and as a bonus you get to learn how part of the fast-fourier transform derivation works.

Upvotes: 1

catwalk
catwalk

Reputation: 6476

for Perl 5:

use List::MoreUtils 'pairwise';
@sum = pairwise { $a + $b } @arr1, @arr2;

Upvotes: 31

daotoad
daotoad

Reputation: 27173

You've seen a C style for loop, and pairwise. Here's an idiomatic Perl for loop and map:

my @arr1 = ( 1, 0, 0, 0, 1 );
my @arr2 = ( 1, 1, 0, 1, 1 );

my @for_loop;
for my $i ( 0..$#arr1 ) { 
    push @for_loop, $arr1[$i] + $arr2[$i];
}

my @map_array = map { $arr1[$_] + $arr2[$_] } 0..$#arr1;

I like map and pairwise best. I'm not sure that I have a preference between those two options. pairwise handles some boring details of plumbing for you, but it is not a built-in like map. On the other hand, the map solution is very idiomatic, and may be opaque to a part-time perler.

So, no real wins for either approach. IMO, both pairwise and map are good.

Upvotes: 6

rjp
rjp

Reputation: 1958

Fundamentally, no, you can't do it without "looping through arrays" because you need to access every element of both arrays in order to sum them. Both the answers so far just hide the looping under a layer of abstraction but it's still there.

If you're concerned about looping over very large arrays, it's probably best to consider other ways of keeping the sum up-to-date as you go.

Upvotes: 8

Richard Stelling
Richard Stelling

Reputation: 25665

If you're using Perl 6:

@a = (1 0 0 0 1) <<+>> (1 1 0 1 1)  #NB: the arrays need to be the same size

The Perl 6 Advent Calendar has more examples.

Upvotes: 8

Related Questions