Unos
Unos

Reputation: 1361

Perl - passing references to a subroutine

I am facing difficulty understanding references in perl. Here is a short perl script to explain my problem [I ran this code using perl-5.8.3]:

#!/usr/bin/perl -w
use strict;
use Data::Dumper;

my %a = ("a" => 1, "b" => 2);
my %b = ();
print Dumper(\%a, \%b);
foo(\%a, \%b);
print "+==After fn call==+\n";
print Dumper(\%a, \%b);
print "+-----------------------+\n";
bar(\%a, \%b);
print "+==After fn call==+\n";
print Dumper(\%a, \%b);

sub foo {
    my($h1, $h2) = @_;
    $h2 = $h1;
    print Dumper($h2);
}

sub bar {
    my($h1, $h2) = @_;
    %{$h2} = %{$h1};
}

I guess in both subroutines, $h1 and $h2 are local vars. Still, bar() actually changes value of original %b, while foo() does not. Why is that?

Upvotes: 0

Views: 2013

Answers (4)

ikegami
ikegami

Reputation: 385655

$h2 is a lexical var that holds a reference. Changing $h2 just replaces the reference therein.

%{$h2} is the hash referenced by $h2 (aka %b), so changing %{$h2} (aka %b) changes the hash referenced by $h2 (aka %b).

You seem to be expecting that changing one variable ($h2) will change another (%b), but I have no idea why you have any such expectation. They're not even the same variable type! How can one even try to change the elements of a hash by changing a scalar when a scalar doesn't have elements (at least not in the same sense as a hash does).

Upvotes: 2

newacct
newacct

Reputation: 122429

This has nothing to do with passing or subroutines. That just confuses the issue. You'll understand it better if you consider the same code without calling subroutines:

#!/usr/bin/perl -w
use strict;
use Data::Dumper;

my %a = ("a" => 1, "b" => 2);
my %b = ();
print Dumper(\%a, \%b);
my $h1 = \%a;
my $h2 = \%b;
$h2 = $h1;
print "+==After fn call==+\n";
print Dumper(\%a, \%b);
print "+-----------------------+\n";
$h1 = \%a;
$h2 = \%b;
%{$h2} = %{$h1};
print "+==After fn call==+\n";
print Dumper(\%a, \%b);

Upvotes: 0

David W.
David W.

Reputation: 107040

Eric Strom is correct, but let's see if we can explain this another way:

sub foo {
    my($h1, $h2) = @_;
    $h2 = $h1;

}

Let's make things easier: $h1 points to memory location #1 and $h2 points to memory location #2. Your statement $h2 = $h1 now makes $h2 point to memory location #1 too.

Has the contents of memory location #1 changed? No. Has the contents of memory location #2 changed? No.

Once you leave the subroutine, $h1 and $h2 no longer exist.

sub bar {
    my($h1, $h2) = @_;
    %{$h2} = %{$h1};
}

When you say %{$h1}, you're now talking about the contents of memory location #1. What you're doing in your assignment is copying the contents of memory location #1 to memory location #2. Notice that $h1 still points to memory location #1 and $h2 still points to memory location #2. Thus, the values of $h1 and $h2 don't change, but what they point to does change.

Now, let's look at %a and %b. The contents of %a were in memory location #1 and the contents of %b were in memory location #2. In sub foo, we didn't change the information in memory location #2, so the value of %b didn't change.

In sub bar, we messed with the contents of memory location #2, so the value of %b (which stores its contents in memory location #2) has changed.

By the way, note that changing %a after the subroutine call doesn't change %b at all. They might share the same contents, but they aren't the same variable.

Upvotes: 1

Eric Strom
Eric Strom

Reputation: 40142

sub foo {
    my($h1, $h2) = @_;  # copy two hash references into lexicals
    $h2 = $h1;          # copy the value in lexical $h1 into $h2
                        # $h2 looses its binding to the hash ref
    print Dumper($h2);
}

this is the same exact behavior you would get if the values contained strings or any other simple value.

sub bar {
    my($h1, $h2) = @_;  # copy two hash references into lexicals
    %{$h2} = %{$h1};    # the hash referred to by $h1 is unpacked into a list
                        # the hash referred to by $h2 is exposed as an lvalue
                        # the assignment operator installs the rhs list into 
                        # the lvalue, replacing any previous content
}

So basically, in the first example, you are just dealing with values, and normal value semantics apply. In the second case, you are dereferencing the values, which turns them back into their advanced types (in this case a HASH).

Upvotes: 6

Related Questions