Perl - 将子引用传递给子例程

问题描述:

我在perl中遇到困难。下面是一个简短的perl脚本解释我的问题[我跑用perl-5.8.3验证码]:Perl - 将子引用传递给子例程

#!/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()则没有。这是为什么?

+1

,因为它不是真的* *参照打电话:如果你考虑相同的代码,而无需调用子程序你会更好地理解它。参考值按值传递给子例程。 –

+4

@布莱恩罗奇,不正确。 Perl *总是*通过引用传递。引用被引用传递,然后被复制到'$ h1'和'$ h2'中。改变'$ h1'和'$ h2'没有效果,因为它们是词法变量。更改'$ _ [0]'和'$ _ [1]'会影响传递给子的变量。 – ikegami

+0

@ikegami,我用'$ _ [1] = $ _ [0]'在'foo'中替换了'$ h2 = $ h1'。输出不会改变。我猜它仍然是按值复制的,所以子程序内部的变化不会影响被引用的变量。我的理解是否正确?还是我仍然想念一些东西? – Unos

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)。

$h2是一个词汇变量,它包含一个引用。更改$h2只是取代其中的参考。

%{$h2}是由$h2(又名%b)引用的散列,所以改变%{$h2}(又名%b)改变由$h2(又名%b)引用的哈希值。

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

埃里克·斯特罗姆是正确的,但让我们看看我们能不能解释一下这个另一种方式:

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中)的值已更改。

顺便说一句,请注意,在子程序调用完全不会改变%b之后更改%a。他们可能会共享相同的内容,但它们不是同一个变量。

这与传递或子程序无关。这只是混淆了这个问题。

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