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

Greetings fellow monks, I'm trying to follow strict programming rules in a particular project I'm busy with, something I don't do often :-) I'm trying to minimise the use of global variables, and only to use local variables in procedures. I now have a situation where I need to pass an entire hash to another procedure. Its easy querying the hash in a global variable, but I'd like this to be done professional. My first attempt failed, which was something like this...
. . &callsub(%hashtopass); sub callsub { %hashread = $_[0]; foreach $k (keys %hashread) { . . . } }
Any idea how I can pass an entire hash as a parameter to a sub? Thanks!

Replies are listed 'Best First'.
Re: Passing hashes to subs?
by hotshot (Prior) on Aug 01, 2002 at 11:31 UTC
    A more effiecient and smart to do is pass the hash by reference, as follows:
    &callsub(\%hashtopass); sub callsub { my %hashread = %{$_[0]}; foreach $k (keys(%hashread)) { } }


    Thanks.

    Hotshot
Re: Passing hashes to subs?
by Chady (Priest) on Aug 01, 2002 at 11:33 UTC

    You cannot pass the hash as you did without distroying it. It is being flattened into a list. Pass it by reference :

    callsub(\%hashtopass); sub callsub { my $hashref = shift; foreach my $k (keys %$hashref) { .. ..

    read more about references


    He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.

    Chady | http://chady.net/
Re: Passing hashes to subs?
by crenz (Priest) on Aug 01, 2002 at 11:35 UTC

    You can use

    &callsub(%hashtopass); sub callsub { %hashread = @_; foreach $k (keys %hashread) { print "$k => $hashread{$k}\n"; } }

    but I prefer to use references:

    &callsub(\%hashtopass, $otherStuff); sub callsub { my ($hashref, $otherParam) = @_; # or: my $hashref = shift; if you prefer that foreach my $k (keys %{$hashref}) { print "$k => $hashref->{$k}\n"; } }

    This way, you can even pass along other parameters.

      &callsub(%hashtopass); sub callsub { %hashread = @_;

      I don't think this is good practice.. what if you decide to pass a scalar along with that and forgot to shift it out before assigning the list to the hash? you might end up with a corrupted list, or worst, a legitimate list with wrong values shooting yourself in the foot.


      He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.

      Chady | http://chady.net/

        Yes, that's why I suggested the second approach using references. But I neglected to point out the dangers to the original poster. Thanks!

        FWIW, I do like to use it if (and only if...) the function is designed to receive only named parameters. E. g.

        sub create { my $self = shift; my %p = @_; print "$p{color} p{animal}" for (1..$p{count}); } $zoo->create(animal => 'camel', count => '10', color => 'green');

        One could opt to create the zoo using a hash reference as in

        $zoo->create({animal => 'monkey', ...});

        but I guess that is a matter of style.

Re: Passing hashes to subs?
by particle (Vicar) on Aug 01, 2002 at 12:01 UTC
    many monks above have provided the wisdom you seek. see perlref and perlsub for more details.

    i want to comment on your use of 'strict programming rules'. the code you provide does not use strict. this, along with warnings (-w on pre-5.006 perls) is your best defense against common programming mistakes. see Use strict warnings and diagnostics or die for more info.

    also, you should be careful when using the & sigil. you must understand its behaviour, as it does not perform as you probably expect. if called with no parenthesis, as in &callsub;, the current @_ is passed automatically. also, it will override your prototypes, if you're using them (but you probably shouldn't be). see perlsub for much, much more on that.

    ~Particle *accelerates*

Re: Passing hashes to subs?
by moxliukas (Curate) on Aug 01, 2002 at 11:40 UTC

    You can pass the entire hash as a hash reference. This would also make things more efficient. Here is the sample code:

    my %hash_outside; $hash_outside{'first'} = 'first value'; $hash_outside{'second'} = 'second value'; sub blah { my %hash = %{ +shift; }; foreach my $k (keys %hash) { print "$k -> " . $hash{$k} . "\n"; } } &blah(\%hash_outside);

    Here you pass a hash reference to the sub by appending a backslash ( \%hash_outside ). First line in the sub dereferences the reference to the hash so you can use it as a normal hash.

    You could also look at perlref documentation to find out more about references.

Re: Passing hashes to subs?
by flocto (Pilgrim) on Aug 01, 2002 at 11:41 UTC
    Besides the options mentioned above (using references..) you can also do either of these:

    foo (%hash); sub foo { my %passed_hash = @_; # do stuff }

    ...which only workes if the hash is the only argument you have (or the last one..). If it is not, or you have more than one hash to pass, and your subroutine is seen at sompile ime, you can use this elegant solution:

    sub foo (\%$); foo (%hash, $arg); sub foo (\%$) { my $hash_ref = shift; my $passed_arg = shift; my $passed_hash = %$hash_ref; # do stuff }

    The second example provides a prototype for the subroutine. How these work can be read in perlsub.. But to give you some clue: It automatically converts the hash into a reference..

    Regards,
    -octo

Re: Passing hashes to subs?
by c0bra (Acolyte) on Aug 01, 2002 at 15:16 UTC