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

10,120

Solution 1

When you pass an array (or hash) to a subroutine, the subroutine will get a list of the values (or key values pairs). That's why you cannot pass two arrays (or two hashes), because the subroutine won't know where the first array ends and the second one starts.

To work around this problem, you should pass in references instead:

my %hash1 = ( foo => 'bar' );
my %hash2 = ( bar => 'baz' );
subroutine( \%hash1, \%hash2 );

sub subroutine {
    my ( $hashref1, $hashref2 ) = @_;
    print $hasref1->{foo}, $hashref2->{bar};
}

PS: Apart from the conceptual problem, your code also features this:

my %d  = @_;     
my %i  = @_;     

If %d and %i are both assigned the same value, it shouldn't come as a surprise when they are the same afterwards.

Solution 2

You might want to check out my book Intermediate Perl, about a third of which deals with references and how to work with them. This includes passing complex data structures into subroutines as well as other ways that references make your life easier. :)

some_sub( \%hash );
some_sub( { key => 'value' } );
some_sub( $hash_ref );

sub some_sub {
    my( $hash_ref ) = @_;

    ...
    }

Solution 3

When you pass in %diet and %iq, they both get flattened into the arg array, so in your print_result, %d contains all items in %diet and %iq.

To solve, use references of the %diet and %iq:

print_result($id, \%diet, \%iq);

Then in print_result:

my $id = shift;
my %d  = %{+shift};
my %i  = %{+shift};
Share:
10,120
Admin
Author by

Admin

Updated on June 24, 2022

Comments

  • Admin
    Admin almost 2 years

    I have a function that is doing some calculations and then passes some properties into another subroutine like so:

    sub get_result {
        my $id = 1;     
        my %diet = ( result  => 28, 
                     verdict => 'EAT MORE FRUIT DUDE...'     
                   );
    
        my %iq = ( result   => 193, 
                   verdict => 'Professor Einstien'   
                 );           
        print_result($id, %diet, %iq);
    }
    
    sub print_result {     
        my $id = shift;     
        my %d  = @_;     
        my %i  = @_;     
    
        print "IQ: $id\n";     
        print "DIET RESULT: $d{result}\n";     
        print "DIET VERDICT: $d{verdict}\n";     
        print "IQ RESULT: $i{result}\n";     
        print "IQ VERDICT: $i{verdict}\n";     
    }     
    

    My problem is that the results printed in (DIET RESULT, DIET VERDICT) and (IQ SCORE, IQ RESULT) are both the same. As if variable %d and %i are being populated with the same variables. Any ideas why this is?

    If I try shifting all three variables like so:

    my $id = shift;     
    my %d  = shift;     
    my %i  = shift; 
    

    I get the following error:

    Odd number of elements in hash assignment
    
  • user2584401
    user2584401 almost 15 years
    Your example is going to look for a hash named shift! Add parens after shift so the interpreter can tell you want a function call.
  • Michael Carman
    Michael Carman almost 15 years
    %i isn't empty, it's a copy of %d because both are initialized from @_. See Manni's answer.
  • Chas. Owens
    Chas. Owens almost 15 years
    This creates shallow copies of both hashes. This means that changes to the first level of the hash will not show up in the original, but changes in the second or later levels will. It looks like the hashes are simple hashes, so this should be fine for now (so long as any changes to the hashes are not expected to propagate). If you need deep copies, then take a look at the Storable module's dclone function (perldoc.perl.org/Storable.html).
  • Admin
    Admin almost 15 years
    This worked perfect. I think I was misinterpreting references between the various types. (Scalar, Array, Hash) Thanks Manni
  • brian d foy
    brian d foy almost 15 years
    How about making the prototype answer not the first one they'd see? :)
  • C. K. Young
    C. K. Young almost 15 years
    Thanks all for helpful comments! I've incorporated Matt's and Michael's changes (though in perhaps unexpected ways :-P). Chas: Personally for my own code I'd rather work with the hash reference directly than shallow-copy even the first layer, but given the OP's original question (which passed the hashes directly into the arglist), this replicates the behaviour a little more "faithfully", for whatever that's worth (in the context of this post). :-P
  • innaM
    innaM almost 15 years
    It might be helpful for others if you accepted the answer then. (Not that I would mind the 25 reputation points in any way.)