podian has asked for the wisdom of the Perl Monks concerning the following question:

Here is an interesting issue I have with perl:
#!/usr/local/bin/perl use strict; my @ar; sub test_args{ my (@ar,$t) = @_; print "t = $t\n"; print "ar = @ar\n"; } push (@ar,"one"); push (@ar,"two"); test_args(@ar,"three");
The above code prints this
t =
ar = one two three
I think the reason is that perl treats all the elements < passed to a subroutine as an array.
Is there any other way to pass the arguments?
If I switch the arguments it works, but wondering what is the reason behind this design intention.
Thanks

Replies are listed 'Best First'.
Re: passing array as an argument
by davido (Cardinal) on Oct 30, 2003 at 21:59 UTC
    Parameters are flattened into a single list. If you want to pass an array and a scalar, you have to pass the scalar first: ($t, @ar) instead, or pass the array by reference.

    You may learn how to use subroutines in Perl by having a look at perlsub. It's worth the 20 minute read if you plan to do much with Perl. You can even skip the more advanced sections on prototypes and overriding built-in functions. Most of what you're going to need comes before that point in the POD.


    Dave


    "If I had my life to live over again, I'd be a plumber." -- Albert Einstein

      Another thing is that, for performance reason, even there is only one parameter, it is still a good idea to pass array ref, instead of array.

      (davido didn't mention this simply because this was not asked in the original post)

      my @numb = (1..100); print sum(\@numb); sub sum { my $numb = shift; my $total; foreach (@$numb) {$total += $_}; return $total; }
        Another thing is that, for performance reason, even there is only one parameter, it is still a good idea to pass array ref, instead of array.

        Well, that's not strictly true. Perl passes by reference by default. Consider:

        perl -le 'sub scare { $_[0] = "boo!" } my $me; scare($me); print $me'
        Often people subvert this by copying the parameters themselves. Code like this is more common than not:
        sub foo { my @args = @_; # ... do stuff ... }
        And usually, it's fine because argument lists aren't very big.

        As for passing a single large array, though, there isn't much reason to pass a reference other than to be able to name it something nice in the sub rather than work directly with @_.

        -sauoq
        "My two cents aren't worth a dime.";
        
Re: passing array as an argument
by hardburn (Abbot) on Oct 30, 2003 at 22:01 UTC

    The @ar array greedily slurps all the data from the right value array (@_). To fix it, you can either reverse the order passed, or use references.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

Re: passing array as an argument
by hmerrill (Friar) on Oct 30, 2003 at 22:17 UTC
    When you need to pass arrays or hashes to a subroutine, learn to pass them as references - it will save you a lot of headaches. As others have already said, the parameter list gets flattened out into a list of scalars, but if all you pass to the subroutine is scalars (references are scalars), then you are all right.
    #!/usr/local/bin/perl use strict; my @ar; sub test_args{ my ($ar_arrayref, $t) = @_; print "t = $t\n"; print "ar = @$ar_arrayref\n"; print "element 0 of array \@ar = $ar_arrayref->[0]\n"; ### ### or create a new array from the reference ### my @test_ar = @$ar_arrayref; print "element 0 of array \@test_ar = $test_ar[0]\n"; } push (@ar,"one"); push (@ar,"two"); test_args(\@ar,"three"); ----------------------------- output: t = three ar = one two element 0 of array @ar = one element 0 of array @test_ar = one
    HTH.
Re: passing array as an argument
by Roy Johnson (Monsignor) on Oct 30, 2003 at 22:37 UTC
    You could, if you really wanted to, pass the arguments as you like, and receive them differently:
    sub test_args { my $t = pop(@_); my @ar = @_; ...
    or even
    sub test_args { my ($t, @ar) = (pop(@_), @_); ...
    In general, if a list you're assigning to contains an array, the array isn't going to leave anything for the variables after it.

    Here's a more bletcherous way, just for interest's sake:

    sub test_args { my (@ar, $t); (@ar[0..$#_-1], $t) = @_; ...
    You can't declare an array slice, so the declaration had to be separated from the assignment. Since I'm assigning to a slice, there are a known number of elements in my list, which allows $t to get the leftovers. Eh, maybe it's not all that bletcherous.