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

Dav*_*d B 10 perl parameter-passing pass-by-reference subroutine

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

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

所以,如果我想foo()改变x,我必须传递它的参考x.

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

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

数组元素也是如此:

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

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

Eri*_*rom 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
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

$$alias[1]++;  # $y is now 3
Run Code Online (Sandbox Code Playgroud)

或别名的切片:

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

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

正如tchrist在对另一个答案的评论中提到的那样,当你使用任何Perl的别名结构时@_,$_他们提供给你的也是原始子程序参数的别名.如:

sub trim {s!^\s+!!, s!\s+$!! for @_}  # in place trimming of white space
Run Code Online (Sandbox Code Playgroud)

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

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

sub add_2 {
    add_1(@_) for 1 .. 2;
}
Run Code Online (Sandbox Code Playgroud)


Eth*_*her 11

这些都在perldoc perlsub中详细记录.例如:

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

  • 有人应该提到别名是可传递的,例如`for(@_){s/^\s + //; s /\s + $ //}`. (2认同)