了解Perl中的数据结构

3
我试图理解perldsc文档中的“常见错误”部分。当作者提到以下内容时,他想传达什么:
“构建类似于数组的数组时最常见的两个错误是意外计算元素数量或多次引用同一内存位置。这里是你只得到计数而不是嵌套数组的情况:”。
for my $i (1..10) {
    my @array = somefunc($i);
    $AoA[$i] = @array;      # WRONG!
}

据我了解,当它迭代时,它会取(1..10)中的第一个值,即1,并将其传递给以下函数:

my @array = somefunc(1);

由于该函数未定义,我将创建逻辑。

sub somefunc  {
my $a = shift;
print $a * $a;
}

这将基本上会做到以下几点:
1 * 1

结果将会是'1'。

据我理解,我的 @array 将会长成这样:

@array = ('1');

接下来的代码将执行:

$AoA[$i] = @array;

我假设$AoA[$1]是一个匿名数组(作者没有用'my'声明),而@array将是这个匿名数组的第一个元素,但作者说这是错误的。并且foreach循环将迭代到“2”。

somefunc(2);

将会是'4'并传递给:
$AoA[$i] = @array

这段代码有什么问题,作者的意图是什么?我正在尝试理解其中的错误,更重要的是,我正在努力理解这段代码。任何帮助都将不胜感激。

更新

我认为我明白了为什么这是一个常见的错误,因为当我使用print和Dumper时,我可以直观地看到作者想要传达的内容,以下是修订后的代码。

#!/usr/bin/perl -w
use strict;
use Data::Dumper;

for my $i (1..10) {
           my @AoA;
           my @array = somefunc($i);
           print "The array is Dumper(@array)\n";
           $AoA[$i] = @array;      # WRONG!
           print Dumper($AoA[$i]);
       }


sub somefunc  {
my $a = shift;
return $a * $a;
}

在perldoc perldsc的“常见错误”段落中,作者指出:

以下是只获取计数而非嵌套数组的情况:

下面是Dumper的输出。

The array is Dumper(1)
$VAR1 = 1;
The array is Dumper(4)
$VAR1 = 1;
The array is Dumper(9)
$VAR1 = 1;
The array is Dumper(16)
$VAR1 = 1;
The array is Dumper(25)
$VAR1 = 1;
The array is Dumper(36)
$VAR1 = 1;
The array is Dumper(49)
$VAR1 = 1;
The array is Dumper(64)
$VAR1 = 1;
The array is Dumper(81)
$VAR1 = 1;
The array is Dumper(100)
$VAR1 = 1;

我假设重复出现的

$VAR1 = 1;

这里的count是指计数,而不是嵌套数组。

作者确实指出,如果我真正想要的是计数,那么可以按照以下方式重写代码:

#!/usr/bin/perl -w
use strict;
use Data::Dumper;

for my $i (1..10) {
           my @count;
           my @array = somefunc($i);
           print "The array is Dumper(@array)\n";
           $count[$i] = scalar @array;      
           print Dumper($count[$i]);
       }


sub somefunc  {
my $a = shift;
return $a * $a;
}

但是文档没有告诉我如何获取嵌套数组?

更新

如果我错了,请纠正我,但我重写了代码以获取嵌套数组:

#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my @count;
my @new_array;

for my $i (1..10) {
           #my @count;
           my @array = somefunc($i);
           push @new_array, [@array];
       }


sub somefunc  {
my $a = shift;
return $a * $a;
}

print Dumper(\@new_array);

这是关于打印的问题

$VAR1 = [
      [
        1
      ],
      [
        4
      ],
      [
        9
      ],
      [
        16
      ],
      [
        25
      ],
      [
        36
      ],
      [
        49
      ],
      [
        64
      ],
      [
        81
      ],
      [
        100
      ]
    ];

2
我认为应该是 return $a * $a 而不是 print $a * $a?而 $AoA[$i] 是指常规数组 @AoA 的第 $i 个元素(作者没有声明)。赋值 $AoA[$i] = @array 处于标量上下文中,因此 @array 将求出数组的长度。 - Håkon Hægland
1
关于 "@array将是匿名数组的第一个元素" 这句话,不对。在标量上下文中,@array 的值为 @array 中元素的数量。 - ikegami
1
请注意,此示例旨在演示如何将@array放置在@AoA的元素中,因此sub somefunc { my $i = shift; return ( $i*2, $i*3 ); }将作为更好的示例。 - ikegami
“文档没有告诉我如何获取嵌套数组”,但我们很多答案都可以。 $ AoA [$i] = \@ array;。 使用Dumper打印$AOA并查看结果。 - Dave Cross
3个回答

6
在下面的语句中:
$AoA[$i] = @array;
@array标量语境 中被引用,产生其元素的数量。这个语境是由 LHS 强制实施的,即 $AoA[$i],它是 @AoA 数组的单个元素。
在 Perl 中,严格来说没有数组的数组。这些本质上是通过“展平”数组或具有引用的数组来模拟的。对于后者,您需要使用取地址操作符,如下所示:
$AoA[$i] = \@array;

首先,你可能会发现Data::Dumper在检查复杂的数据结构(例如数组引用和哈希引用)方面非常方便。


1
Perl是多态的,这意味着它可以透明地处理不同的数据类型,并且通常能够相当准确地判断如何处理某个东西。这使得程序员的工作更加容易,因为它不像其他语言那样强类型化。
所以例如,如果$i是数字4,你可以这样做:
print $i + 1;

你会看到一个5 - 相当合乎逻辑,对吧?

如果你这样做:

print "I am " , $i , " years old";

你会看到"I am 4 years old" - 在这种情况下,Perl说"你正在使用列表上下文,所以我将把$i视为字符串。无需将数字转换为字符串,因为许多其他语言坚持要求这样做。

所以当你赋值时

$AoA[$i] = @array;

它对待这个的方式取决于上下文。在标量上下文中,它将设置 $AoA[$i] 为数组的长度。

有关标量与列表上下文的更多信息,请阅读此答案:

http://perl.plover.com/context.html


好观点,花太多时间在JavaScript上了。本应该使用“.”运算符,但这样就明确地进行了连接 :) - Mikkel
1
你正在列表上下文中操作,所以我会将 $i 视为字符串。列表上下文并不表示一个值是被视为数字还是字符串。是 print() 将所有参数转换为字符串。如果你用点号替换逗号,那么 $i 将在标量上下文中被评估,但仍将被转换为字符串(这次是由连接运算符而不是 print() 进行的)。 - Dave Cross

1

你的示例并不能很好地帮助理解这里发生了什么,因为你的子程序总是返回“1”-调用print()的结果。如果你将print()替换为return(),那么你将得到不同的值(1、4、9等)。

但下一行代码:

$AoA[$i] = @array;

我会始终将@Aoa的元素分配为1。这是因为您正在将一个数组(@array)分配给标量变量($AoA[$i]),当您在标量上下文中评估数组时,您会得到数组中的元素数。

现在,由于您的@array只有一个元素,因此您可以这样做:

$AoA[$i] = $array[0];

但这并不是真正地构建一个数组的数组。你真正想要做的是获取对数组的引用。

$AoA[$i] = \@array;

如果您的子程序返回多个值,那么它将更加有用。
sub somefunc  {
  # Used $x instead of $a as $a has a special meaning in Perl
  my $x = shift;
  return ($x * $x, $x * $x * $x);
}

for my $i (1..10) {
  my @array = somefunc($i);
  $AoA[$i] = \@array;
}

作为探索的有用工具,Data::Dumper 是不错的选择。尝试添加以下内容:
use Data::Dumper;

将以下代码添加到您的代码顶部并保存:

到您的代码顶部:

print Dumper @AoA;

foreach循环后,查看不同的数据结构返回。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接