eold eold - 7 months ago 10
Perl Question

Passing two or more arrays to a Perl subroutine

I am having trouble passing and reading arguments inside subroutine which is expected to have two arrays.

sub two_array_sum { # two_array_sum ( (1 2 3 4), (2, 4, 0, 1) ) -> (3, 6, 3, 5)
# I would like to use parameters @a and @b as simply as possible
}

# I would like to call two_array_sum here and pass two arrays, @c and @d


I have seen and tried several examples from the web, but none of them worked for me.

Answer

There are two ways you can do this:

  1. by prototype
  2. by reference

But before I discuss these--if what you show in your question is about the extent of what you want to do--let me suggest List::MoreUtils::pairwise

So, where you would write this:

my @sum = two_array_sum( @a, @b )

You'd simply write this:

my @sum = pairwise { $a + $b } @a, @b;

By prototype

This works like push. (And just like push it demands to have a @ sigil on something)

sub two_array_sub (\@\@) { 
    my ( $aref, $bref ) = @_;
    ...
}

That way when you do this

two_array_sub( @a, @b );

it works. Whereas normally it would just show up in your sub as one long list. They aren't for everybody as you'll see in my discussion below.

By reference

That's the way that everybody is showing you.

some_sub( \@a, \@b );

About prototypes

They're finicky. This won't work if you have refs:

two_array_sub( $arr_ref, $brr_ref );

You have to pass them like this:

two_array_sub( @$arr_ref, @$brr_ref );

However, because making "array expressions" gets really ugly quickly with arrays nested deep, I often avoid Perl's fussiness as you can overload the type of reference Perl will take by putting it in a "character class" construct. \[$@] means that the reference can either be a scalar or array.

sub new_two_array_sub (\[$@]\[$@]) { 
    my $ref = shift;
    my $arr = ref( $ref ) eq 'ARRAY' ? $ref : $$ref; # ref -> 'REF';
    $ref    = shift;
    my $brr = ref( $ref ) eq 'ARRAY' ? $ref : $$ref;
    ...
}

So all these work:

new_two_array_sub( @a, $self->{a_level}{an_array} );
new_two_array_sub( $arr, @b );
new_two_array_sub( @a, @b );
new_two_array_sub( $arr, $self->{a_level}{an_array} );

However, Perl is still fussy about this... for some reason:

new_two_array_sub( \@a, $b );
OR 
new_two_array_sub( $a, [ 1..3 ] );

Or any other "constructor" that still could be seen as a reference to an array. Fortunately, you can shut Perl up about that with the old Perl 4 &

&new_two_array_sub( \@a, [ 1..3 ] );

Then the mux-ing code in the sub takes care of handling two array references.