Perl - 将引用传递给子例程

时间:2011-11-16 19:19:41

标签: perl pass-by-reference

我在理解perl中的引用时遇到困难。这是一个简短的perl脚本来解释我的问题[我使用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};
}

我想在两个子程序中,$ h1和$ h2都是本地变量。仍然,bar()实际上改变了原始%b的值,而foo()则没有。那是为什么?

4 个答案:

答案 0 :(得分:6)

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);
}

如果值包含字符串或任何其他简单值,则这与您将获得的行为完全相同。

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
}

所以基本上,在第一个例子中,您只是处理值,并且正常值语义适用。在第二种情况下,您将取消引用这些值,这会将它们变回高级类型(在本例中为HASH)。

答案 1 :(得分:2)

$h2是一个包含引用的词法变量。更改$h2只会替换其中的引用。

%{$h2}$h2引用的哈希(又名%b),因此更改%{$h2}(又名%b)会更改{{1引用的哈希值(又名$h2)。

您似乎期望更改一个变量(%b)会改变另一个变量($h2),但我不知道您为何会有这样的期望。他们甚至不是相同的变量类型!当标量没有元素时(至少与散列的意义不同),如何通过更改标量来尝试更改散列元素。

答案 2 :(得分:1)

Eric Strom是正确的,但让我们看看我们是否可以用另一种方式解释:

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

}

让我们更轻松:$h1指向内存位置#1,$h2指向内存位置#2。您的语句$h2 = $h1现在使$h2也指向内存位置#1。

内存位置#1的内容是否已更改?否。内存位置#2的内容是否已更改?否。

离开子例程后,$h1$h2不再存在。

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

当你说%{$h1}时,你现在正在谈论内存位置#1的内容。您在作业中所做的是将内存位置#1的内容复制到内存位置#2。请注意,$h1仍指向内存位置#1,$h2仍指向内存位置#2。因此,$h1$h2的值不会改变,但他们指出的内容会发生变化。

现在,让我们看一下%a%b%a的内容位于内存位置#1,%b的内容位于内存位置#2。在sub foo中,我们未更改内存位置#2中的信息,因此%b的值未发生变化。

sub bar中,我们弄乱了内存位置#2的内容,因此%b(将其内容存储在内存位置#2中)的值发生了变化。

顺便提一下,请注意在子程序调用后更改%a根本不会更改%b。它们可能共享相同的内容,但它们不是同一个变量。

答案 3 :(得分:0)

这与传递或子程序无关。这只是混淆了这个问题。如果你在不调用子程序的情况下考虑相同的代码,你会更好地理解它:

#!/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);