为什么从@_ 转移的值不是别名?

Why shifted value from @_ is not alias?

shift
Shifts the first value of the array off and returns it...

这样做是为了优化速度并避免按值复制。

也在perlsub

The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated

因此,如果我们在 sub 中执行 my $self = shift,我们会将第一个值从 @_ 移开,这是一个别名,不是吗?

但是当我们比较这两个时:

sub test {
     print $_[0];    # SCALAR(0xf73c38)
     my $x =  shift;
     print $x;       # SCALAR(0xf79800)
}

我们看到$x是副本。

为什么来自@_ 的 shifted 值不是别名?

因此,如果也为案例 my $x = shift 复制该值,它比 my $x = $_[0] 有什么好处?

移位后的值是一个别名

$ perl -E 'sub F{say $_[0]; say \shift} $x=42; say $x; F($x)'
SCALAR(0x1d7f1e0)
SCALAR(0x1d7f1e0)
SCALAR(0x1d7f1e0)

赋值操作(例如,$x = $_[0]$x = shift)在右侧创建标量的副本,因此新赋值不再是别名。

正如 toolic 所说,shift 的好处是修改了 @_,这有时会使您在子程序的其余部分更容易使用。

如果您仍然希望能够修改输入

,您仍然可以使用对移位值的引用
$ perl -E 'sub G { my $x=\shift; $$x = 19 } my $z = 42; G($z); say $z'
19

shift 不是左值运算符。

$ perl -e'
   use feature qw( say );
   sub f { shift = 456; }
   { my $x = 123; f($x); say $x; }
'
Can't modify shift in scalar assignment at -e line 3, near "456;"
Execution of -e aborted due to compilation errors.

但是,从 @_ 转移的值仍然是别名。

$ perl -e'
   use feature qw( say );
   sub f { my $p = \shift; $$p = 456; }
   { my $x = 123; f($x); say $x; }
'
456

$ perl -e'
   use feature qw( say );
   sub f { ${ \shift } = 456; }
   { my $x = 123; f($x); say $x; }
'
456

问题是您将别名数组元素的值赋给了 $x,而不是将 $x 别名赋给了数组元素。如果要命名 @_ 的元素,则需要为其指定一个变量的别名。

$ perl -e'
   use feature qw( say );
   sub f { our $x; local *x = \shift; $x = 456; }
   { my $x = 123; f($x); say $x; }
'
456

$ perl -e'
   use feature qw( say );
   use Data::Alias qw( alias );
   sub f { alias my $x = shift; $x = 456; }
   { my $x = 123; f($x); say $x; }
'
456

$ perl -e'
   use feature qw( say refaliasing );
   no warnings qw( experimental::refaliasing );
   sub f { \my $x = \shift; $x = 456; }
   { my $x = 123; f($x); say $x; }
'
456

my $x = shift 相对于 my $x = $_[0]; 的好处很多,尽管很小。

  • shift$_[0].
  • 更容易输入
  • 当有多个参数时,您可以对每个参数使用 shift 而不是计数。
  • shift 支持复杂和开放式参数列表。 (例如 sub f { my $cb = shift; $cb->($_) for @_; }
  • shift 在微观上比 $_[0] (IIRC) 快。