How can I pass two arrays and a string to a Perl subroutine?

16,080

Solution 1

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";

}

Solution 2

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...)

Solution 3

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 );

Solution 4

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";

}
Share:
16,080
Alfons
Author by

Alfons

Updated on July 16, 2022

Comments

  • Alfons
    Alfons almost 2 years

    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.
    

  • DVK
    DVK over 13 years
    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) - stackoverflow.com/questions/3991474/…