tags:

views:

77

answers:

4

How can I pass two arrays and a string to a sub?

Here's what I'm trying to do:

use strict;
use warnings;

my @x = qw(AAAA BBBB CCCC DDDD EEEE);
my @y = qw(1111 2222 3333 4444 5555);

my $z = "hello";

Hello(@x,@y,$z);

exit(0);

sub Hello {

    my (@x,@y,$z) = @_;

    print "$_\n" for @x;
    print "$_\n";
    print "$_\n" for @y;
    print "$_\n";
    print "$z\n";
}

Output:

AAA
BBBB
CCCC
DDDD
EEEE
1111
2222
3333
4444
5555
hello
Use of uninitialized value $_ in concatenation (.) or string at test.pl line 19.

Use of uninitialized value $_ in concatenation (.) or string at test.pl line 21.

Use of uninitialized value $z in concatenation (.) or string at test.pl line 22.

+8  A: 

You need to pass each of the arrays as a reference, otherwise your @x in the sub will gobble up the ENTIRE array of arguments, leaving @y an empty arraay and $z an undef value.

This happens because the comma operator - in a list context - will turn a @x, @y, $z into a single array consisting of all the elements of @x followed by all elements of @y and then a value of $z; your @x in the sub will gobble up the ENTIRE combined array of arguments, leaving @y an empty array and $z an undef value.

Another possible source of confusion is the fact that you named both variables @x, despite the fact that they are completely independent of each other due to scoping rules. Good practice would be to name them something distinct to avoid having to guess which one you meant to use, e.g. call the subroutine's first array @x2.

Please note that you can pass the array as a reference in one of two ways - the reference to the original array (real pass-by-reference approach) as well as reference to a COPY of the array - which will behave like you wanted your origainal code to behave and pass by value.

use strict; use warnings;

my @x = qw(AAAA BBBB CCCC DDDD EEEE); 
my @y = qw(1111 2222 3333 4444 5555);
my $z = "hello";

Hello(\@x,\@y,$z);

# If you wish to pass a reference of a COPY of the array, 
# so that you can modify it inside the subroutine without modifying the original,
# instead call Hello([@x], [@y], $z);

exit(0);

sub Hello {

    my ($x2,$y2,$z2) = @_;
    # Now, you de-reference array reference $x2 via @$x2 or $x2->[$i]
    # where previously you used @x2 or $x2[$i]
    print "$_\n" for @$x2;
    print "$_\n";
    print "$_\n" for @$y2;
    print "$_\n";
    print "$z2\n";

}
DVK
+2  A: 

You need to pass in array references:

sub Hello {
  my ( $x_ref, $y_ref, $z ) = @_;
  my @x = @$x_ref;
  my @y = @$y_ref;
  ...
}

Hello( \@x, \@y, $z );
CanSpice
+4  A: 

you need to use references:

sub Hello {
   my ($x, $y, $z) = @_;
   print "$_\n" for @$x;
   ...
}

Hello(\@x, \@y, $z);

you can also use Perl's subroutine prototypes to eliminate the \ at the call site:

sub Hello (\@\@$) {...}

Hello(@x, @y, $z);

The (\@\@$) prototype tells the compiler that the arguments to Hello will have array reference context on the first two args, and scalar context on the third arg. Prototypes are not for argument validation, they are to allow you to write subroutines that work like the builtins. In this case, like push.

As DVK mentions in a comment below, PBP recommends against using prototypes as a general rule, because they tend to be used improperly for argument validation. I feel that using certain features of prototypes (\% or \@ to impose reference context for plural types, or & as a first arg to allow map like block syntax) justifies breaking with PBP. Particularly true is if the sub is being used to extend Perl's syntax (helper functions, functions that are just syntactic sugar...)

Eric Strom
@Eric - in the interest of full disclosure, Perl Best Practices book discourages using sub prototypes (I'd up-vote your answer if it was clarified thusly) - http://stackoverflow.com/questions/3991474/is-using-prototypes-to-declare-array-reference-context-on-subroutine-args-a-good/3991558#3991558
DVK
+1  A: 
use strict; use warnings;

my @x = qw(AAAA BBBB CCCC DDDD EEEE); my @y = qw(1111 2222 3333 4444 5555);

my $z = "hello";

Hello(\@x,\@y,$z);

exit(0);

sub Hello {
    my ($x,$y,$z) = @_;

    print "$_\n" for @$x;
    print "$_\n" for @$y;
    print "$z\n";

}
bob.faist