8

我有类似于以下的 Perl 代码:

# -- start --

my $res;

# run query to fetch IPv6 resources
while( my $row = $org_ip6_res->fetchrow_arrayref )
{
    if( $row->[4] =~ /PA/ ) {
        $res->{ipv6}{pa}{$row->[2]}++;
    } elsif( $row->[4] eq 'PI' ) {
        $res->{ipv6}{pi}{$row->[2]}++;
    }
}

# -- stop --

$res在迭代查询结果之前从未设置过任何一点,但代码运行得很好。

当我在每个值之前放置打印语句时,在这两种情况下我都会得到空白,但如果打印语句是在应用增量之后出现的,我会得到 >= 1 的值,具体取决于组织拥有多少 IPv6 资源。

我的问题是,我是否认为这意味着 Perl 中未初始化的哈希键的值自动为零?

抱歉,如果它是一个新手问题,但我只是不熟悉这样的构造,即$hashref->{foo}->{bar}++ 尚未明确分配值的地方$hashref->{foo}->{bar}。提前致谢!

4

4 回答 4

26

该值不会自动为零。该值最初是未定义的。然而,如果你把它当作一个数字来对待(例如,应用++到它上面),那么 Perl 就会把它当作零来对待。如果你把它当作一个字符串来对待(例如,应用.到它),那么 Perl 就会把它当作一个空字符串来对待。

来自perldoc perlsyn,在“声明”下:

您需要在 Perl 中声明的唯一内容是报告格式和子例程(有时甚至不需要子例程)。一个变量保存未定义的值(“undef”),直到它被分配一个定义的值,它不是“undef”。当用作数字时,“undef”被视为0;当用作字符串时,它被视为空字符串,“”;当用作未分配给的引用时,它被视为错误。

于 2009-05-01T14:28:49.477 回答
5

为了详细说明 Telemachus 的帖子,未初始化的值将是未定义的。结构的深层部分是自动激活的。这是一个方便的功能,可以自动为您创建数据结构。当你想要它时,自动激活非常棒,但当你想要阻止它时,它可能会很痛苦。网上有很多关于理解自动生存的教程、文章和帖子。

所以给定一个未定义的$refand $ref->{ipv6}{pa}{'foo'}++$ref将被赋值为:

$ref = { 
     ipv6 => { 
          pa => { 
              foo => undef
          }
     }
};

然后 undef 将被递增,因为 undef 计数为 0,所以我们得到 0++,即 1。对于最终结果:ref->{ipv6}{pa}{'foo'} == 1

如果您启用了警告,(您这样做use warnings;,不是吗?)当您对这些未定义的值进行操作时,您将收到“未初始化的值”警告。如果增加未初始化的值是所需的行为,那么您可以在代码的有限部分上关闭所需的警告组:

use strict;
use warnings;
my $res;

// run query to fetch IPv6 resources
while( my $row = $org_ip6_res->fetchrow_arrayref )
{   no warnings 'uninitialized';
    if( $row->[4] =~ /PA/ ) {
        $res->{ipv6}{pa}{$row->[2]}++;
    } elsif( $row->[4] eq 'PI' ) {
        $res->{ipv6}{pi}{$row->[2]}++;
    }
}

您可以在perllexwarn中找到警告层次结构。

于 2009-05-01T15:11:35.053 回答
4

它基本上是未定义的,但是当你增加它时它被视为零。

Perl 用语中的术语是“autovivified”。

您可能想要做的是使用exists 关键字

$res->{ipv6}{pa}{$row->[2]}++ if exists($res->{ipv6}{pa}{$row->[2]});
于 2009-05-01T14:34:44.423 回答
2

没有未初始化的哈希之类的东西。可以未初始化的是特定键的值。哈希值只是一个标量值;它与类似的变量没有什么不同$foo

您的示例中有几个不同的 Perl 功能在交互。

最初$res是未定义的(即它具有值undef)。当您使用未初始化的值作为散列引用时(如 中$res->{ipv6}...),Perl 将其“自动激活”为一个。也就是说,Perl 创建一个匿名散列并用undef对新散列的引用替换 的值。每次您使用结果值作为参考时,此过程都会(无声地)重复。

最终,您以自己的方式自动激活到$res->{ipv6}{pa}{$row->[2]},这是未定义的。请记住,这只是一个标量值,例如$foo. 行为和说的一样

my $foo;
$foo++;

当您使用未定义的值时,Perl 会做一些特殊的事情。如果将它们用作数字,Perl 会将它们转换为 0。如果将它们用作字符串,Perl 会将它们转换为 ''(空字符串)。在大多数情况下,如果您启用了警告(您应该这样做),您将收到“使用未初始化的值...”警告。不过,自增运算符 ( ++) 是一种特殊情况。为方便起见,它在递增值之前默默地将值从转换为undef0

于 2009-05-01T18:03:27.667 回答