Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a hash by reference in perl

Tags:

perl

I know this should be easily searchable on google, not to mention a trivial use of perl, but I've tried many solutions I've found and so far none of them gives the expected behavior. Essentially, I'm trying to call a subroutine, return a reference to a hash from that subroutine, pass a reference to that hash to another subroutine, and then print the contents of that hash, via code similar to the following:

#!/usr/bin/perl                                                                                                                                                                                                   

my $foo = make_foo();

foreach $key (sort keys %$foo) {
    print "2 $key $$foo{$key}\n";
}

print_foo(\%foo);

sub print_foo
{
    my %loc = ???;
    foreach $key (sort keys %loc}) {
        print "3 $key $loc{$key}\n";
    }
}

sub make_foo
{
    my %ret;
    $ret{"a"} = "apple";
    foreach $key (sort keys %ret) {
        print "1 $key $ret{$key}\n";
    }
    return \%ret;
}

Can someone tell me the best way of doing this (via subroutines) without creating an extra copy of the hash? The solutions I've tried have not printed out any lines starting with "3".

like image 353
jonderry Avatar asked Dec 12 '11 21:12

jonderry


2 Answers

You have to pull the parameters in as a reference and then dereference it:

sub print_foo
{
    my ($loc) = @_;
    foreach my $key (sort keys %$loc) {
       print "3 $key $loc->{$key}\n";
    }
}

Anytime you make a reference, you have to explicitly dereference it. Also, beware that any changes to the reference will change the original. If you want to avoid changing the original, you can either pass the hash as a list:

print_foo(%foo); # flattens the hash to a list and passes it in through @_

sub print_foo
{
    my (%loc) = @_; # load the hash from the list
    foreach my $key (sort keys %loc) {
       print "3 $key $loc{$key}\n";
    }
}

Or copy the hash reference into a new hash:

sub print_foo
{
    my ($ref_loc) = @_; # changes to %$ref_loc will change %foo
    my %loc = %$ref_loc; # COPY, changes to %loc do not change %foo
    foreach my $key (sort keys %loc}) {
       print "3 $key $loc{$key}\n";
    }
}
like image 97
zostay Avatar answered Oct 11 '22 11:10

zostay


Everything else is good, so I'm just going to concentrate on print_foo...

sub print_foo
{
    my %loc = %{shift @_};
    foreach $key (sort keys %loc) {
        print "3 $key $loc{$key}\n";
    }
}

FYI, you need to realize that %loc is now a COPY of the hash (I asked a question which explains this here: Confusion about proper usage of dereference in Perl ). To avoid making a copy...

sub print_foo
{
    my $loc = shift @_;
    foreach $key (sort keys %$loc) {
        my $val = $loc->{$key}
        print "3 $key $val\n";
    }
}
like image 27
Dave Avatar answered Oct 11 '22 11:10

Dave