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

I'm not very sure how to pass multiple type of parameters into subroutine.
#! /usr/bin/perl use strict; use warnings; sub user (\@\%) { my ($age, $home) = @_; return (@$age, %$home); } my @age = (20, 30); my %home = (Kelvin => "Tokyo", Andrew => "New York"); my (@age_r, %home_r) = user(@age,%home); print @age_r;
When I print @age_r, it returns 2030KelvinTokyoAndrewNew York. I think it should be just 2030? Any problem with my coding? Thanks!

Replies are listed 'Best First'.
Re: Subroutine Parameters
by GrandFather (Saint) on May 31, 2007 at 02:55 UTC

    If you:

    my (@array, ...) = (list);

    @array gets the entire contents of the list. Your sub flattens (@$age, %$home) into a list of scalars and they all end up in @age_r. You don't even need the sub to be involved. Consider:

    use strict; use warnings; my @age = (20, 30); my %home = (Kelvin => "Tokyo", Andrew => "New York"); my (@age_r, %home_r) = (@age, %home); print "@age_r";

    prints:

    20 30 Kelvin Tokyo Andrew New York

    However:

    ... my ($age_r, $home_r) = (\@age, \%home); print "@$age_r";

    prints:

    20 30

    so for your sub you should return a list of references then use the syntax as shown above. In your sub you simply:

    return ($age, $home);

    DWIM is Perl's answer to Gödel
      Thanks GrandFather, I get what you mean!
Re: Subroutine Parameters
by Util (Priest) on May 31, 2007 at 03:34 UTC

    ++GrandFather. I would add that, since you will need to work with references anyway, get rid of the (\@\%) prototypes (which are rarely a good idea), and pass references directly, like this: user( \@age, \%home ).

    Bonus bug waiting to happen: Once you place Kelvin and Andrew into the hash %home, they lose their ordering. If you intend to match the 20 to Kelvin and 30 to Andrew, you will not be able to do this using only the info in %home and @age!

Re: Subroutine Parameters
by graff (Chancellor) on May 31, 2007 at 03:45 UTC
    In your code snippet, the "user" sub was written to expect an array reference and a hash reference as its args, but when you called the sub, you forgot to put backslashes in front of the args, and so you passed the full list of array values and hash keys and values instead.

    I suspect that was a copy/paste error when you posted the code. If it had been that way in the script that you actually ran, I think you would have gotten an error message (something about "can't treat a string (or number?) as an array reference") instead of the flattened list that GrandFather explained about.

    Anyway, you probably don't need to be doing the prototyping thing in the sub declaration.

      In your code snippet, the "user" sub was written to expect an array reference and a hash reference as its args, but when you called the sub, you forgot to put backslashes in front of the args, and so you passed the full list of array values and hash keys and values instead. I suspect that was a copy/paste error when you posted the code. If it had been that way in the script that you actually ran, I think you would have gotten an error message (something about "can't treat a string (or number?) as an array reference")

      Not quite - prototypes silently coerce arguments to match prototypes in this case. In perlsub:

      Any backslashed prototype character represents an actual argument th +at absolutely must start with that character. The value passed as part + of @_ will be a reference to the actual argument given in the subroutin +e call, obtained by applying "\" to that argument.

      Grandfather was actually explaining about the return assignment:

      my (@age_r, %home_r) = user(@age,%home);

      --chargrill
      s**lil*; $*=join'',sort split q**; s;.*;grr; &&s+(.(.)).+$2$1+; $; = qq-$_-;s,.*,ahc,;$,.=chop for split q,,,reverse;print for($,,$;,$*,$/)

      The call works because the prototype forces the arguments to be references. From the Prototypes section in perlsub:

      Any backslashed prototype character represents an actual argument that absolutely must start with that character. The value passed as part of @_ will be a reference to the actual argument given in the subroutine call, obtained by applying \ to that argument.

      However, in general it is best to avoid prototypes exactly because they cause this sort of confusion.


      DWIM is Perl's answer to Gödel