处理丢失的引用

时间:2017-07-13 20:16:00

标签: arrays perl sorting hash

我在Perl脚本中收到错误Can't use an undefined value as an ARRAY reference。以下是高度简化的版本。

基本上我已经设置了一个数组哈希,其中一些可能是空的(在这种情况下为B)。如果我不对数据数组进行排序,它的工作正常。我已经尝试添加一个条件来测试该特定数组是否存在,但它在此设置中并不喜欢。在数组填充时对它进行排序并不容易(与此示例不同)。

use strict;
use warnings;

my @list = ('A', 'B', 'C');
my %data_for;
$data_for{'A'} = ['apple', 'astronaut', 'acorn'];
$data_for{'C'} = ['car', 'cook', 'candy'];

# Creates error
foreach my $letter (@list) {
    print "$letter: ";
    foreach my $item ( sort @{$data_for{$letter}}) {
        print "$item, ";
    }
    print "\n";
}

这是我想要的输出(似乎很明显,但是呃):

A: acorn, apple, astronaut, 
B:
C: candy, car, cook,

另外,如果我首先打印工作版本(没有排序),那么排序的第二个版本可以正常运行而不会出错。我不明白这一点,但我可以将其作为一种解决方法。

2 个答案:

答案 0 :(得分:4)

$data_for{B}未定义,因此尝试将其取消引用为数组(在sort @{$data_for{$letter}}表达式中)是一个错误。

一种解决方法是为$data_for{B}分配一个(空)值:

$data_for{'A'} = ['apple', 'astronaut', 'acorn'];
$data_for{'B'} = [];
$data_for{'C'} = ['car', 'cook', 'candy'];

但更常见的解决方法是使用'||'运算符(或Perl的'//'运算符> = v5.10)在未定义哈希值时指定默认值

foreach my $item ( sort @{$data_for{$letter} || []}) { ... }

如果$data_for{$letter}未定义(因此计算结果为false),则表达式$data_for{$letter} || []将计算为对空数组的引用,并且数组取消引用操作将成功。

答案 1 :(得分:2)

因此,您需要检查$data_for{$letter}是否为引用。可能的检查:

  • exists($data_for{$letter})
  • defined($data_for{$letter})因为未定义的哈希元素未定义。
  • $data_for{$letter}因为引用始终为真。

我们可以使用支票为我们提供取消引用的内容:

sort @{ $data_for{$letter} // [] }

我们可以使用支票来避免解除引用。

sort $data_for{$letter} ? @{ $data_for{$letter} } : ()

这给了我们以下内容:

print "$letter: ";
for my $item ( sort $data_for{$letter} ? @{ $data_for{$letter} } : () ) {
    print "$item, ";
}
print "\n";

或者更好的是,我们可以使用以下内容避免跟踪,

use feature qw( say );

say "$letter: ", join ", ", sort $data_for{$letter} ? @{ $data_for{$letter} } : ();
  

如果我不对数据数组进行排序,它的工作正常。

当您取消引用用作可修改值(左值)的未定义变量时,Perl将自动创建相应类型的变量,并且它将在未定义变量中放置对新创建的变量的引用。这被称为“autovivification”。

由于自动更新仅发生在左值上下文中,因此在其他地方解除未定义的值会导致错误。

 my ($a,$b);  # Both are undefined.
 @$a = @b;    # OK. Equivalent to @{ $a //= [] } = @b.
 @a = @$b;    # XXX. "Can't use an undefined value as an ARRAY reference."

sort不会修改其操作数,因此它不会在左值上下文中对它们进行求值。

foreach循环在左值上下文中计算其操作数以允许for (@$a) { $_ = uc($_); }。这意味着

for (@{ $data_for{$letter} }) { ... }

隐式修改$data_for{$letter},如下所示:

for (@{ $data_for{$letter} //= [] }) { ... }