masterial
masterial

Reputation: 2216

How do I pass a hash to subroutine?

Need help figuring out how to do this. My code:

my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};


&printInfo(%hash);

sub printInfo{
   my (%hash) = %_;
   foreach my $key (keys %_{       
    my $a = $_{$key}{'Make'};   
    my $b = $_{$key}{'Color'};   
    print "$a $b\n";
    }
}

Upvotes: 30

Views: 54749

Answers (5)

Robert P
Robert P

Reputation: 15988

You're so very, very close. There is no %_ for passing hashes, it must be passed in @_. Luckily, Hashes are assigned using a list context, so

sub printInfo {
   my %hash = @_;
   ...
}

will make it work!

Also note, using the & in front of the subroutine call has been, in most cases, unnecessary since at least Perl 5.000. You can call Perl subroutines just like in other languages these days, with just the name and arguments. (As @mob points out in the comments, there are some instances where this is still necessary; see perlsub to understand this more, if interested.)

Upvotes: 9

David W.
David W.

Reputation: 107090

The best way to pass hashes and arrays is by reference. A reference is simply a way to talk about a complex data structure as a single data point -- something that can be stored in a scalar variable (like $foo).

Read up on references, so you understand how to create a reference and dereference a reference in order to get your original data back.

The very basics: You precede your data structure with a backslash to get the reference to that structure.

my $hash_ref   = \%hash;
my $array_ref  = \@array;
my $scalar_ref = \$scalar;   #Legal, but doesn't do much for you...

A reference is a memory location of the original structure (plus a clue about the structure):

print "$hash_ref\n";

Will print something like:

HASH(0x7f9b0a843708)

To get the reference back into a useable format, you simply put the reference into the correct sigil in front:

my %new_hash = %{ $hash_ref };

You should learn about using references since this is the way you can create extremely complex data structures in Perl, and how Object Oriented Perl works.


Let's say you want to pass three hashes to your subroutine. Here are the three hashes:

my %hash1 = ( this => 1, that => 2, the => 3, other => 4 );
my %hash2 = ( tom => 10, dick => 20, harry => 30 );
my %hash3 = ( no => 100, man => 200, is => 300, an => 400, island => 500 );

I'll create the references for them

my $hash_ref1 = \%hash1;
my $hash_ref2 = \%hash2;
my $hash_ref3 = \%hash3;

And now just pass the references:

mysub ( $hash_ref1, $hash_ref2, $hash_ref3 );

The references are scalar data, so there's no problem passing them to my subroutine:

sub mysub {
    my $sub_hash_ref1  = shift;
    my $sub_hash_ref2  = shift;
    my $sub_hash_ref3  = shift;

Now, I just dereference them, and my subroutine can use them.

    my %sub_hash1 = %{ $sub_hash_ref1 };
    my %sub_hash2 = %{ $sub_hash_ref2 };
    my %sub_hash3 = %{ $sub_hash_ref3 };

You can see what a reference is a reference to by using the ref command:

my $ref_type = ref $sub_hash_ref;    # $ref_type is now equal to "HASH"

This is useful if you want to make sure you're being passed the correct type of data structure.

sub mysub {
    my $hash_ref = shift;

    if ( ref $hash_ref ne "HASH" ) {
        croak qq(You need to pass in a hash reference);
    }

Also note that these are memory references, so modifying the reference will modify the original hash:

my %hash = (this => 1, is => 2, a => 3 test => 4);
print "$hash{test}\n";   # Printing "4" as expected
sub mysub ( \%hash );    # Passing the reference
print "$hash{test}\n";   # This is printing "foo". See subroutine:


sub mysub { 
    my $hash_ref = shift;

    $hash_ref->{test} = "foo";    This is modifying the original hash!
}

This can be good -- it allows you to modify data passed to the subroutine, or bad -- it allows you to unintentionally modify data passed to the original subroutine.

Upvotes: 9

Axeman
Axeman

Reputation: 29854

You can pass them as

  1. The argument list do_hash_thing( %hash )
  2. A reference to the hash in the argument list `do_hash_thing( @args_before, \%hash, @args_after )
  3. As a reference by prototype, working like keys and other hash operators.

The list works like so:

sub do_hash_thing {
    my %hash = @_;
    ...
}

do_hash_thing( %hash );

This also allows you to "stream" hash arguments as well:

do_hash_thing( %hash_1, %hash_2, parameter => 'green', other => 'pair' );

By reference works like this:

sub do_hash_thing { 
    my $hash_ref = shift;
    ...
}

do_hash_thing( \%hash, @other_args );

Here by prototype (\%@). The prototype makes perl look for a hash in the first argument and pass it by reference.

sub do_hash_thing (\%@) { 
    my $hash_ref = shift;
    ...
}

do_hash_thing( %hash => qw(other args) );
# OR
do_hash_thing %hash => qw(other args);

Caveat: prototypes don't work on methods.

Upvotes: 3

Sven Eppler
Sven Eppler

Reputation: 1706

The easy way, which may lead to problems when the code evolves, is simply by assigning the default array @_ (which contains all key-value-pairs as an even list) to the %hash which then rebuilds accordingliy. So your code would look like this:

sub printInfo {
   my %hash = @_;
   ...
}

The better way would be to pass the hash as reference to the subroutine. This way you could still pass more parameters to your subroutine.

printInfo(\%hash);
sub PrintInfo {
   my %hash = %{$_[0]};
   ...
}

An introduction to using references in Perl can be found in the perlreftut

Upvotes: 45

René Nyffenegger
René Nyffenegger

Reputation: 40613

I believe you want

my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};

printInfo(%hash);

sub printInfo{
   my %hash = @_;
   foreach my $key (keys %hash){       
    my $a = $hash{$key}{'Make'};   
    my $b = $hash{$key}{'Color'};   
    print "$a $b\n";
   }
}

In the line printInfo(%hash) the %hash is expanded to a list with the alternating key-value pairs.

In printInfo, the @_ is this list that, and assigned to %hash it creates again the keys with their corresponding value from the alternating elements in the list.

Upvotes: 6

Related Questions