无需分配即可散列附加值

时间:2019-02-20 19:13:24

标签: perl

我有一个包含2个散列的脚本,在打印出内容时,我发现该脚本正在为第二个散列分配一个值,而我没有这样做。我先读取第一个哈希,然后读取第二个,然后再读取整个第二个哈希。它在hash2中只应包含1个条目,但现在包含2个条目。如何在这里分配hash2中的James值?

my %hash1 = ();
my %hash2 = ();

$hash1{"James"}{"1 Main Street"}++;
$hash1{"John"}{"2 Elm Street"}++;
$hash2{"John"}{"3 Oak Street"}++;

foreach my $name (keys %hash1) {
 print "Hash1  Name $name\n";
 foreach my $address (keys %{$hash1{$name}}) {
   print "Hash1  Address $address\n";
   foreach my $address (keys %{$hash2{$name}}) {
     print "Hash2  Address $address\n";
   }
 } 
}

print "\n";
foreach my $name (keys %hash2) {
 print "Hash2  Name $name\n";
 foreach my $address (keys %{$hash2{$name}}) {
   print "Hash2  Address $address\n";
 }
}

输出看起来像这样:

Hash1  Name James
Hash1  Address 1 Main Street
Hash1  Name John
Hash1  Address 2 Elm Street
Hash2  Address 3 Oak Street

Hash2  Name James
Hash2  Name John
Hash2  Address 3 Oak Street

2 个答案:

答案 0 :(得分:1)

当您尝试从哈希2读取不存在的密钥时,正在创建第二个值。

my %hash1 = ();
my %hash2 = ();

$hash1{"James"}{"1 Main Street"}++;
$hash1{"John"}{"2 Elm Street"}++;
$hash2{"John"}{"3 Oak Street"}++;

foreach my $name (keys %hash1) {
 print "Hash1  Name $name\n";
 foreach my $address (keys %{$hash1{$name}}) {
   print "Hash1  Address $address\n";
   next unless exists $hash2{$name}; # check if the key exists in second hash before trying to use the key in $hash2
   foreach my $address (keys %{$hash2{$name}}) { #second value gets created here
     print "Hash2  Address $address\n";
   }
 } 
}

print "\n";
foreach my $name (keys %hash2) {
 print "Hash2  Name $name\n";
 foreach my $address (keys %{$hash2{$name}}) {
   print "Hash2  Address $address\n";
 }
}

答案 1 :(得分:1)

当您使用未定义的值作为参考时,Perl进行所需的参考排序,然后尝试执行该操作。这称为“自动生存”。

这是一个小示范。变量以未定义开始。然后,将其视为数组引用(取消获取第0个元素的引用):

use Data::Dumper;

my $empty;
print Dumper( $empty );

my $value = $empty->[0];
print Dumper( $empty );

Perl将$empty转换为数组引用,然后尝试从中获取第0个元素。剩下的是以前曾经有undef的数组引用:

$VAR1 = undef;
$VAR1 = [];

这是预期的行为。

再走一步。将undef放入数组中,然后将该元素视为数组引用:

use Data::Dumper;

my @array = ( 1, undef, 'red' );
print Dumper( \@array );

my $value = $array[1]->[0];
print Dumper( \@array );

现在第二个元素是一个空数组引用:

$VAR1 = [
          1,
          undef,
          'red'
        ];
$VAR1 = [
          1,
          [],
          'red'
        ];

再走一步。不要存储undef值。而是访问数组中最后一项之后的数组位置:

use Data::Dumper;

my @array = ( 1, 'red' );
print Dumper( \@array );

my $value = $array[2]->[0];
print Dumper( \@array );

现在,您在数组中获得一个数组引用元素。现在再增加一个元素:

$VAR1 = [
          1,
          'red'
        ];
$VAR1 = [
          1,
          'red',
          []
        ];

如果您走得更远(例如,元素5),则用undef将填满元素直到所需的元素:

use Data::Dumper;

my @array = ( 1, 'red' );
print Dumper( \@array );

my $value = $array[5]->[0];
print Dumper( \@array );

$VAR1 = [
          1,
          'red'
        ];
$VAR1 = [
          1,
          'red',
          undef,
          undef,
          undef,
          []
        ];

散列的工作方式相同,这就是您所看到的。当您要检查James下是否有第二级密钥时,Perl需要创建James密钥,并为其提供一个空的哈希引用值以进行检查。该二级密钥不存在,但是“ James”的一级密钥仍然存在:

use Data::Dumper;

my %hash = (
    John => { Jay => '137' },
    );
print Dumper( \%hash );

if( exists $hash{James}{Jay} ) {
    print $hash{James}{Jay};
    }
print Dumper( \%hash );

现在您会看到一个额外的键:

$VAR1 = {
          'John' => {
                      'Jay' => '137'
                    }
        };
$VAR1 = {
          'James' => {},
          'John' => {
                      'Jay' => '137'
                    }
        };

在这种情况下,您不喜欢此功能,但是可以使用no autovivification编译指示将其关闭。这是您需要首先安装的CPAN模块:

no autovivification;
use Data::Dumper;

my %hash = (
    John => { Jay => '137' },
    );
print Dumper( \%hash );

if( exists $hash{James}{Jay} ) {
    print $hash{James}{Jay};
    }
print Dumper( \%hash );

您没有得到额外的钥匙:

$VAR1 = {
          'John' => {
                      'Jay' => '137'
                    }
        };
$VAR1 = {
          'John' => {
                      'Jay' => '137'
                    }
        };

您可能还想阅读How can I check if a key exists in a deep Perl hash?。我展示了一种方法,可让您检查嵌套的哈希而不创建中间级别。