How can I use hashes as arguments to subroutines in Perl?
Asked Answered
S

5

26

I was asked to modify some existing code to add some additional functionality. I have searched on Google and cannot seem to find the answer. I have something to this effect...

%first_hash = gen_first_hash();
%second_hash = gen_second_hash();
do_stuff_with_hashes(%first_hash, %second_hash);

sub do_stuff_with_hashes
{
    my %first_hash = shift;
    my %second_hash = shift;

    # do stuff with the hashes
}

I am getting the following errors:

Odd number of elements in hash assignment at ./gen.pl line 85.
Odd number of elements in hash assignment at ./gen.pl line 86.
Use of uninitialized value in concatenation (.) or string at ./gen.pl line 124.
Use of uninitialized value in concatenation (.) or string at ./gen.pl line 143.

Line 85 and 86 are the first two lines in the sub routine and 124 and 143 are where I am accessing the hashes. When I look up those errors it seems to suggest that my hashes are uninitialized. However, I can verify that the hashes have values. Why am I getting these errors?

Sour answered 21/7, 2009 at 18:54 Comment(1)
This is a duplicate. Please take a look at stackoverflow.com/questions/944784Soma
O
28

The hashes are being collapsed into flat lists when you pass them into the function. So, when you shift off a value from the function's arguments, you're only getting one value. What you want to do is pass the hashes by reference.

do_stuff_with_hashes(\%first_hash, \%second_hash);

But then you have to work with the hashes as references.

my $first_hash  = shift;
my $second_hash = shift;
Obverse answered 21/7, 2009 at 18:56 Comment(0)
I
17

A bit late but,

As have been stated, you must pass references, not hashes.

do_stuff_with_hashes(\%first_hash, \%second_hash);

But if you need/want to use your hashes as so, you may dereference them imediatly.

sub do_stuff_with_hashes {
    my %first_hash = %{shift()};
    my %second_hash = %{shift()};
};
Induce answered 27/7, 2009 at 14:49 Comment(1)
to be clear, I'm not dereferencing the hashes, I'm copying them.Induce
D
9

Hash references are the way to go, as the others have pointed out.

Providing another way to do this just for kicks...because who needs temp variables?

do_stuff_with_hashes( { gen_first_hash() }, { gen_second_hash() } );

Here you are just creating hash references on the fly (via the curly brackets around the function calls) to use in your do_stuff_with_hashes function. This is nothing special, the other methods are just as valid and probably more clear. This might help down the road if you see this activity in your travels as someone new to Perl.

Diameter answered 21/7, 2009 at 19:11 Comment(1)
Happy to oblige. One thing to keep in mind if you stick around in Perl is that everyone will point out how "There's more than one way to do it". It's kind of a blessing and a curse )depending on your opinion of such things) in Perl programming. Have fun!Diameter
S
7

First off,

 do_stuff_with_hashes(%first_hash, %second_hash);

"streams" the hashes into a list, equivalent to:

 ( 'key1_1', 'value1_1', ... , 'key1_n', 'value1_n', 'key2_1', 'value2_1', ... )

and then you select one and only one of those items. So,

 my %first_hash = shift;

is like saying:

 my %first_hash = 'key1_1'; 
 # leaving ( 'value1', ... , 'key1_n', 'value1_n', 'key2_1', 'value2_1', ... )

You cannot have a hash like { 'key1' }, since 'key1' is mapping to nothing.

Stereotype answered 21/7, 2009 at 19:0 Comment(0)
A
0

A solution without shift() is faster, because it do not copy the data in memory, and you can modify the hash in the subroutine. See my example:

sub do_stuff_with_hashes($$$) {
    my ($str,$refHash1,$refHash2)=@_;
    foreach (keys %{$refHash1}) { print $_.' ' }
    $$refHash1{'new'}++;
}

my (%first_hash, %second_hash);
$first_hash{'first'}++;
do_stuff_with_hashes('any_parameter', \%first_hash, \%second_hash);
print "\n---\n", $first_hash{'new'};
Annulet answered 18/6, 2020 at 8:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.