@_如何在Perl子程序中工作?

时间:2010-11-03 20:25:55

标签: perl pass-by-reference parameter-passing subroutine

我总是确定如果我将一个Perl子程序传递给一个简单的标量,它就永远不会在子程序之外改变它的值。那就是:

my $x = 100;
foo($x);
# without knowing anything about foo(), I'm sure $x still == 100

因此,如果我希望foo()更改x,我必须将x的引用传递给它。

然后我发现事实并非如此:

sub foo {
 $_[0] = 'CHANGED!';
}
my $x = 100;
foo($x);
print $x, "\n"; # prints 'CHANGED!'

对于数组元素也是如此:

my @arr = (1,2,3);
print $arr[0], "\n"; # prints '1'
foo($arr[0]);
print $arr[0], "\n"; # prints 'CHANGED!'

有点让我感到惊讶。这是如何运作的?子例程不是只获得参数的吗?它是如何知道它的地址的?

3 个答案:

答案 0 :(得分:19)

在Perl中,存储在@_中的子例程参数始终是调用站点值的别名。此别名仅在@_中保留,如果您复制了值,那就是您获得的值。

所以在这个子目录中:

sub example {
   # @_ is an alias to the arguments
   my ($x, $y, @rest) = @_;  # $x $y and @rest contain copies of the values
   my $args = \@_;  # $args contains a reference to @_ which maintains aliases
}

请注意,此别名在列表扩展后发生,因此如果您将数组传递给example,则数组会在列表上下文中展开,@_将设置为数组中每个元素的别名(但是数组本身不适用于example)。如果你想要后者,你会传递对数组的引用。

子例程参数的别名是一个非常有用的功能,但必须小心使用。为防止意外修改外部变量,在Perl 6中,您必须指定希望使用is rw的可写别名参数。

一个鲜为人知但有用的技巧是使用此别名功能来创建别名的数组引用

my ($x, $y) = (1, 2);

my $alias = sub {\@_}->($x, $y);

$$alias[1]++;  # $y is now 3

或别名切片:

my $slice = sub {\@_}->(@somearray[3 .. 10]);  

事实证明,使用sub {\@_}->(LIST)从列表创建数组实际上比
[ LIST ]更快,因为Perl不需要复制每个值。当然,缺点(或取决于您的观点的上行)是值仍然是别名,因此您无法在不更改原件的情况下更改它们。

由于 tchrist 在对另一个答案的评论中提到,当您在@_上使用任何Perl的别名结构时,他们为您提供的$_也是一个别名原始子程序参数。如:

sub trim {s!^\s+!!, s!\s+$!! for @_}  # in place trimming of white space

最后所有这些行为都是可嵌套的,所以当在另一个子例程的参数列表中使用@_(或它的一部分)时,它也会获得第一个子例程参数的别名:

sub add_1 {$_[0] += 1}

sub add_2 {
    add_1(@_) for 1 .. 2;
}

答案 1 :(得分:11)

perldoc perlsub详细记录了这一点。例如:

  

传入的任何参数都显示在数组@_中。因此,如果您使用两个参数调用函数,那么这些参数将存储在$ _ [0]和$ _ [1]中。该   array @_是一个本地数组,但它的元素是实际标量参数的别名。特别是,如果元素$ _ [0]被更新,则   更新相应的参数(如果不可更新则发生错误)。如果参数是一个数组或哈希元素,当它不存在时   函数被调用,该元素仅在被修改或(如果)被修改或者对它的引用时创建。 (一些早期版本的Perl创建了   元素是否已分配给元素。)分配给整个数组@_删除该别名,并且不更新任何参数。

答案 2 :(得分:2)

Perl通过引用传递参数,而不是通过值传递参数。见http://www.troubleshooters.com/codecorn/littperl/perlsub.htm