weird behavior when passing hashes to subroutine

Discussion in 'Perl Misc' started by Patrick Hartman, Feb 19, 2010.

  1. Hi all, I was working on a program today and made a sub routine that I
    was passing a few scalars and also a hash to as arguments. This is
    what I initially tried:

    my %foo = qw(pepsi cola doctor pepper seven up);
    my $bar = 'soda is yummy!';

    &drink(%foo,$bar);

    sub drink {
    my (%sodas,$opinion) = @_;

    # do whatever here
    print "yummy!";
    }

    The result was not what I was expecting. The $bar value that I was
    expecting to be the $opinion scalar ended up as a extra key in the
    %sodas hash as undef, and $opinion was empty. I played around a little
    and found that if I reverse the order so the scalar is passed before
    the hash, it worked like I was hoping:

    my %foo = qw(pepsi cola doctor pepper seven up);
    my $bar = 'soda is yummy!';

    &drink($bar,%foo);

    sub drink {
    my ($opinion,%sodas) = @_;

    # do whatever here
    print "yummy!";
    }

    Is there a rule as to what order to pass types to a subroutine? I'm a
    little confused on this one. Thanks,

    Patrick
    Patrick Hartman, Feb 19, 2010
    #1
    1. Advertising

  2. Patrick Hartman

    Guest

    On Thu, 18 Feb 2010 16:43:38 -0800 (PST), Patrick Hartman <> wrote:

    [snip]

    >Is there a rule as to what order to pass types to a subroutine? I'm a
    >little confused on this one. Thanks,
    >
    >Patrick


    There is no rules in Perl, just syntax. If it doesen't work like
    you expect, Perl has no way of knowing, and thinks that's what you intend.
    Use strict/warnings to help you out.

    Basically, I think this is what you did:

    my %sodas = (
    # The never ending list
    'pepsi',
    'cola',
    'doctor',
    'pepper',
    'seven',
    'up',
    'soda is yummy!',
    # Add to the never ending list
    );

    And, you should have gotten an "Odd number of elements ..."
    type of message.

    If your looking for a "I before E, except after C"
    rhyme, I can't think of any.

    Also, you probably don't need ampersand before the function
    call.

    -sln
    , Feb 19, 2010
    #2
    1. Advertising

  3. Patrick Hartman

    John Bokma Guest

    Patrick Hartman <> writes:

    > Hi all, I was working on a program today and made a sub routine that I
    > was passing a few scalars and also a hash to as arguments. This is
    > what I initially tried:
    >
    > my %foo = qw(pepsi cola doctor pepper seven up);
    > my $bar = 'soda is yummy!';
    >
    > &drink(%foo,$bar);
    >
    > sub drink {
    > my (%sodas,$opinion) = @_;
    >
    > # do whatever here
    > print "yummy!";
    > }
    >
    > The result was not what I was expecting. The $bar value that I was
    > expecting to be the $opinion scalar ended up as a extra key in the
    > %sodas hash as undef, and $opinion was empty. I played around a little
    > and found that if I reverse the order so the scalar is passed before
    > the hash, it worked like I was hoping:
    >
    > my %foo = qw(pepsi cola doctor pepper seven up);
    > my $bar = 'soda is yummy!';
    >
    > &drink($bar,%foo);
    >
    > sub drink {
    > my ($opinion,%sodas) = @_;
    >
    > # do whatever here
    > print "yummy!";
    > }
    >
    > Is there a rule as to what order to pass types to a subroutine? I'm a
    > little confused on this one. Thanks,


    You have to be aware that the hash eats up @_ (it's the same if you use
    an array, i.e. my ( @foo, $bar ) = @_; will result in all of @_ ending
    up in @foo).

    You could do:

    sub drink {
    my $opinion = pop @_;
    my %sodas = @_;
    :
    :
    }

    pop removes the last item from @_ (in this case) and assigns it to
    $opionion.

    You can also decide to pass a reference to your hash instead, see

    perldoc perlreftut

    --
    John Bokma j3b

    Hacking & Hiking in Mexico - http://johnbokma.com/
    http://castleamber.com/ - Perl & Python Development
    John Bokma, Feb 19, 2010
    #3
  4. Patrick Hartman

    Guest

    On Thu, 18 Feb 2010 20:21:32 -0600, Don Piven <> wrote:

    > wrote:
    >> On Thu, 18 Feb 2010 16:43:38 -0800 (PST), Patrick Hartman <> wrote:
    >>
    >> [snip]
    >>
    >>> Is there a rule as to what order to pass types to a subroutine? I'm a
    >>> little confused on this one. Thanks,
    >>>
    >>> Patrick

    >>
    >> There is no rules in Perl, just syntax. If it doesen't work like
    >> you expect, Perl has no way of knowing, and thinks that's what you intend.
    >> Use strict/warnings to help you out.
    >>
    >> Basically, I think this is what you did:
    >>
    >> my %sodas = (
    >> # The never ending list
    >> 'pepsi',
    >> 'cola',
    >> 'doctor',
    >> 'pepper',
    >> 'seven',
    >> 'up',
    >> 'soda is yummy!',
    >> # Add to the never ending list
    >> );

    >
    >Well, it would have been more like
    >
    >drink(qw(pepsi cola doctor pepper seven up), 'soda is yummy').
    >
    >That's seven arguments. Now when you do
    >
    >my (%sodas, $opinion) = @_ ;
    >
    >the %sodas hash is going to suck up all the items in the argument list,
    >and leave nothing for $opinion, so 1) $opinion is going to be undef, and
    >2) the last argument is treated as a key, and since there are no more
    >elements, that key gets a value of undef.
    >
    >Reversing the order of arguments ($opinion, %sodas) works because
    >$opinion gets the first element of the list, and %sodas sucks down the
    >rest of 'em in key/value order.
    >
    >> And, you should have gotten an "Odd number of elements ..."
    >> type of message.

    >
    >That's a compile-time error, since the compiler has to know how many
    >elements are in the initializer in order to know if it's an odd number.
    > The compiler won't see a problem with %hash = @_ since it has no clue
    >how long @_ is going to be.
    >
    >Don


    Thanks, next time I won't do that (or do it a different way).

    -sln
    , Feb 19, 2010
    #4
  5. Patrick Hartman <> wrote:
    >Hi all, I was working on a program today and made a sub routine that I
    >was passing a few scalars and also a hash to as arguments. This is
    >what I initially tried:
    >
    >my %foo = qw(pepsi cola doctor pepper seven up);
    >my $bar = 'soda is yummy!';
    >
    >&drink(%foo,$bar);
    >
    >sub drink {
    > my (%sodas,$opinion) = @_;
    >
    > # do whatever here
    > print "yummy!";
    >}
    >
    >The result was not what I was expecting. The $bar value that I was
    >expecting to be the $opinion scalar ended up as a extra key in the
    >%sodas hash as undef, and $opinion was empty.


    From "perldoc perlsub':
    The Perl model for function call and return values is simple: all
    functions are passed as parameters one single flat list of scalars,

    Therefore at the point where you are doing
    my (%sodas,$opinion) = @_;
    there is no recollection that those values in @_ where once organized
    into two distinct variables. And of ourse %s is grabbing as many as it
    can.

    >sub drink {
    > my ($opinion,%sodas) = @_;


    In this case $opinion will grab the first value (it can't grab any
    others bedause its a scalar) and thus leaving the rest of the list for
    %sodas.

    >Is there a rule as to what order to pass types to a subroutine? I'm a
    >little confused on this one.


    That question is not really targeting the root issue. But as a rule of
    thumb if you want to extract arguments like that then they have to have
    a known lenght, i.e. be scalars or hashes or arrays of a known, fixed
    length.
    Then on top of that you can also get away with one single hash or array
    of random length at the very end of the argument list. This is the case
    in your second example.

    If you have more than one of those list-like arguments with an arbitrary
    number of elements then you have to pass references instead or resort to
    awkward workarounds like e.g. passing the number of elements as an
    additional argument.

    jue
    Jürgen Exner, Feb 19, 2010
    #5
  6. Patrick Hartman

    sreservoir Guest

    On 2/18/2010 9:21 PM, Don Piven wrote:
    > wrote:
    >> On Thu, 18 Feb 2010 16:43:38 -0800 (PST), Patrick Hartman
    >> <> wrote:
    >>
    >> [snip]
    >>
    >>> Is there a rule as to what order to pass types to a subroutine? I'm a
    >>> little confused on this one. Thanks,
    >>>
    >>> Patrick

    >>
    >> There is no rules in Perl, just syntax. If it doesen't work like
    >> you expect, Perl has no way of knowing, and thinks that's what you
    >> intend.
    >> Use strict/warnings to help you out.
    >>
    >> Basically, I think this is what you did:
    >>
    >> my %sodas = (
    >> # The never ending list
    >> 'pepsi',
    >> 'cola',
    >> 'doctor',
    >> 'pepper',
    >> 'seven',
    >> 'up',
    >> 'soda is yummy!',
    >> # Add to the never ending list
    >> );

    >
    > Well, it would have been more like
    >
    > drink(qw(pepsi cola doctor pepper seven up), 'soda is yummy').
    >
    > That's seven arguments. Now when you do
    >
    > my (%sodas, $opinion) = @_ ;
    >
    > the %sodas hash is going to suck up all the items in the argument list,
    > and leave nothing for $opinion, so 1) $opinion is going to be undef, and
    > 2) the last argument is treated as a key, and since there are no more
    > elements, that key gets a value of undef.
    >
    > Reversing the order of arguments ($opinion, %sodas) works because
    > $opinion gets the first element of the list, and %sodas sucks down the
    > rest of 'em in key/value order.
    >
    >> And, you should have gotten an "Odd number of elements ..."
    >> type of message.

    >
    > That's a compile-time error, since the compiler has to know how many
    > elements are in the initializer in order to know if it's an odd number.
    > The compiler won't see a problem with %hash = @_ since it has no clue
    > how long @_ is going to be.


    no. it is not.

    perl -We 'sub a { %_ = @_ } a(0)' -> warning
    perl -e 'sub a { %_ = @_ } a(0)' -> nothing

    --

    "Six by nine. Forty two."
    "That's it. That's all there is."
    "I always thought something was fundamentally wrong with the universe"
    sreservoir, Feb 19, 2010
    #6
  7. Patrick Hartman

    Uri Guttman Guest

    >>>>> "PH" == Patrick Hartman <> writes:

    PH> Hi all, I was working on a program today and made a sub routine that I
    PH> was passing a few scalars and also a hash to as arguments. This is
    PH> what I initially tried:

    PH> my %foo = qw(pepsi cola doctor pepper seven up);
    PH> my $bar = 'soda is yummy!';

    PH> &drink(%foo,$bar);

    read perldoc perlsub. that isn't doing what you think it does.

    PH> sub drink {
    PH> my (%sodas,$opinion) = @_;

    actually that is the bad line. the hash will slurp in all of @_ (and
    should give you an odd count warning IF you used warnings which you
    should do). nothing will ever be assigned to $opinion.

    PH> The result was not what I was expecting. The $bar value that I was
    PH> expecting to be the $opinion scalar ended up as a extra key in the
    PH> %sodas hash as undef, and $opinion was empty. I played around a little
    PH> and found that if I reverse the order so the scalar is passed before
    PH> the hash, it worked like I was hoping:

    PH> my %foo = qw(pepsi cola doctor pepper seven up);
    PH> my $bar = 'soda is yummy!';

    PH> &drink($bar,%foo);

    PH> sub drink {
    PH> my ($opinion,%sodas) = @_;

    PH> # do whatever here
    PH> print "yummy!";
    PH> }

    PH> Is there a rule as to what order to pass types to a subroutine? I'm a
    PH> little confused on this one. Thanks,

    no rule about order. just you the first array or hash in the left side
    of any assignment gets all the rest of the values from the right
    side. so if you must have one, it should be the last thing on the left
    side. or use references to pass multiple aggregates around.

    uri

    --
    Uri Guttman ------ -------- http://www.sysarch.com --
    ----- Perl Code Review , Architecture, Development, Training, Support ------
    --------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
    Uri Guttman, Feb 19, 2010
    #7
  8. On Feb 18, 8:11 pm, John Bokma <> wrote:
    > You have to be aware that the hash eats up @_ (it's the same if you use
    > an array, i.e. my ( @foo, $bar ) = @_; will result in all of @_ ending
    > up in @foo).



    Thanks John, that explanation made the most sense. I was unaware that
    using a hash or array gobbled up all the @_ contents, this behavior
    makes sense now I know that.

    Thanks everyone else for the input and suggestions. Hash references
    are still foreign to me (I have seen them but don't really understand
    them). I am still working through the end of the Llama book, but I
    have the Alpaca book waiting on my desk for when I am done. I know it
    covers references so hoping that will give me some clarity.

    I am still in the beginning of the learning process, so I appreciate
    all the help on here.

    Patrick
    Patrick Hartman, Feb 19, 2010
    #8
  9. Patrick Hartman

    Justin C Guest

    On 2010-02-19, Patrick Hartman <> wrote:
    > Hi all, I was working on a program today and made a sub routine that I
    > was passing a few scalars and also a hash to as arguments. This is
    > what I initially tried:
    >
    > my %foo = qw(pepsi cola doctor pepper seven up);
    > my $bar = 'soda is yummy!';
    >
    > &drink(%foo,$bar);


    see 'Pass by Reference' in perdoc perlsub

    The scalar isn't a problem, it's the hash so I'd do:

    &drink(\%foo, $bar); # passes a reference to the hash, not the hash

    (someone will probably come along in a minute and tell you that you
    shouldn't be using the & calling your subs unless you know why you need
    to. I still don't understand this so I keep out of those discussions and
    never use the & anyway).
    >
    > sub drink {

    my ($foo, $bar) = @_;
    > my (%sodas,$opinion) = @_;
    >
    > # do whatever here

    dereference the hashref here (it's all in perlsub)
    > print "yummy!";
    > }
    >
    > The result was not what I was expecting. The $bar value that I was
    > expecting to be the $opinion scalar ended up as a extra key in the
    > %sodas hash as undef, and $opinion was empty. I played around a little
    > and found that if I reverse the order so the scalar is passed before
    > the hash, it worked like I was hoping:
    >
    > my %foo = qw(pepsi cola doctor pepper seven up);
    > my $bar = 'soda is yummy!';
    >
    > &drink($bar,%foo);
    >
    > sub drink {
    > my ($opinion,%sodas) = @_;
    >
    > # do whatever here
    > print "yummy!";
    > }
    >
    > Is there a rule as to what order to pass types to a subroutine? I'm a
    > little confused on this one. Thanks,
    >
    > Patrick



    Justin.

    --
    Justin C, by the sea.
    Justin C, Feb 19, 2010
    #9
  10. Justin C <> wrote:
    > On 2010-02-19, Patrick Hartman <> wrote:
    > > Hi all, I was working on a program today and made a sub routine that I
    > > was passing a few scalars and also a hash to as arguments. This is
    > > what I initially tried:
    > >
    > > my %foo = qw(pepsi cola doctor pepper seven up);
    > > my $bar = 'soda is yummy!';
    > >
    > > &drink(%foo,$bar);


    > see 'Pass by Reference' in perdoc perlsub


    > The scalar isn't a problem, it's the hash so I'd do:


    > &drink(\%foo, $bar); # passes a reference to the hash, not the hash


    But there's one important difference that the OP should keep in
    mind: The original call

    drink(%foo,$bar);

    passes copies of the hash keys and values to the subroutine, thus
    any changes done to them are local to that subroutine and don't
    change anything about the original hash. But when one passes a hash
    by reference then what the function receives is just a pointer to
    that hash and thus all changes done to the hash via the reference
    are done to the original hash in the calling function. So, if the
    hash isn't changed in the function then passing a reference is fine
    (and probably faster since the elements don't have to be copied),
    but if that's not the case then passing a hash reference instead of
    a list of its elements may lead to unexpected effects, i.e. the
    %foo' hash suddenly being changed after a call of drink(). To
    avoid that a copy of the original hash would have to be made and
    a reference to that copy be passed to the function.

    Regards, Jens
    --
    \ Jens Thoms Toerring ___
    \__________________________ http://toerring.de
    Jens Thoms Toerring, Feb 19, 2010
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ben Holness

    Hashes of Hashes via subs

    Ben Holness, Oct 5, 2003, in forum: Perl
    Replies:
    8
    Views:
    561
    Ben Holness
    Oct 8, 2003
  2. Steven Arnold

    using hashes as keys in hashes

    Steven Arnold, Nov 23, 2005, in forum: Ruby
    Replies:
    3
    Views:
    160
    Mauricio Fernández
    Nov 23, 2005
  3. sam
    Replies:
    3
    Views:
    733
    Tad McClellan
    Jan 6, 2005
  4. Tim O'Donovan

    Hash of hashes, of hashes, of arrays of hashes

    Tim O'Donovan, Oct 27, 2005, in forum: Perl Misc
    Replies:
    5
    Views:
    211
  5. king
    Replies:
    5
    Views:
    184
Loading...

Share This Page