数组推送和引用

Array push and references

我是一个初学者 perl 程序员,我遇到了一段代码,如下所示:

my @Numbers=();

push(@{$Numbers[0]},3,8,9);
push(@{$Numbers[1]},5);

print join(",",@{$Numbers[0]});
print "\n";

print join(",",@{$Numbers[1]});
print "\n";

我很难理解推向 @{$Numbers[0]}@{$Numbers[1]} 的目的。推送是否会自动在 Numbers 数组中创建对 [3,8,9][5] 的数组引用?我不难理解下面的语法,我想这更常见,但我没有遇到我在上面粘贴的语法,我猜它也使用自动激活来填充带有引用的 Numbers 数组。

my @Numbers=();

push(@Numbers,[3,8,9]);
push(@Numbers,[5]);

print join(",",@{$Numbers[0]});
print "\n";

print join(",",@{$Numbers[1]});
print "\n";

使用 Data::Dumper 查看发生了什么。

#! /usr/bin/perl

use Data::Dumper;

my @Numbers=();

push(@Numbers,[3,8,9]);
push(@Numbers,[5]);

print Dumper(@Numbers);

输出:

$VAR1 = [
          3,
          8,
          9
        ];
$VAR2 = [
          5
        ];

如果将数组传递给 Dumper,元素将被解释为多个参数。

圆括号是数组,方括号是数组引用。 push 仅适用于数组,不适用于数组引用。有关引用和取消引用的详细信息,请参阅 Perl 手册 perlref

您可以使用反斜杠将数组转换为数组引用:

print Dumper(\@Numbers);

现在 Dumper 显示带有方括号的引用。

$VAR1 = [
          [
            3,
            8,
            9
          ],
          [
            5
          ]
        ];

您也可以将 Numbers 设为引用,但在将其传递给 push 之前必须取消引用它。

#! /usr/bin/perl

use Data::Dumper;

my $Numbers=[];

push(@$Numbers,[3,8,9]);
push(@$Numbers,[5]);

print Dumper($Numbers);

现在你就明白了:

push(@{$Numbers[0]},3,8,9);
  • $Numbers[0] 获取数组的第一个元素(这是一个标量)。
  • @{$Numbers[0]} 将标量取消引用到数组中。
  • push 将三个标量 389 附加到取消引用的数组。

这导致创建数组引用。

有两个特征在起作用,其中一个确实是 autovivification


稍后我将进入自动复活。首先,让我们考虑这样一个事实,即当我们访问 $Numbers[0]@Numbers 是空的。这不是问题。 Perl 将自动扩展数组以包含此元素。[1]

my @a;
$a[3] = 123;          # ok!
print(Dumper(\@a));   # [ undef, undef, undef, 123 ]

这不是文档中所说的自动生成,但它非常相似,以至于有些人也将其称为自动生成。


正如我提到的,第二个功能确实是自动生成。 Autovivification 是通过取消引用自动创建匿名变量。具体来说,它发生在取消引用未定义的标量时。[2]

在这种情况下,自动生成会创建一个数组,因为 @BLOCK 是一个数组解引用。对该数组的引用被创建并存储在以前的 undef 标量中。

换句话说,自动生成

push @{ $ref }, LIST;

实际上等同于

push @{ $ref //= [] }, LIST;

if (!defined($ref)) {
   my @anon;
   $ref = \@anon;
}

push @$ref, LIST;

这可能只是该问题的人为示例,但请注意,发布的代码很奇怪。我们知道 $Numbers[0]$Numbers[1] 是 undef,所以我们不需要 push.

my @nums;
@{ $nums[0] } = ( 3, 8, 9 );    # Also autovivification.
@{ $nums[1] } = 5;

并且因为我们知道 $Numbers[0]$Numbers[1] 是 undef,我们也没有必要依赖自动生成。

my @nums;
$nums[0] = [ 3, 8, 9 ];
$nums[1] = [ 5 ];

让我们摆脱那些硬编码的索引。

my @nums;
push @nums, [ 3, 8, 9 ];
push @nums, [ 5 ];

最后的简化。

my @nums = (
   [ 3, 8, 9 ],
   [ 5 ],
);

  1. 这只发生在左值上下文中,这意味着引用的变量应该是 modified/modifiable。 say $a[@a]; 不会扩展数组,即使它访问最后一个元素之后的元素。

  2. 它也必须在左值上下文中。 my @copy = @{ $undef }; 不会自动激活。