2009-05-01 28 views
8

我已经类似于下面的Perl代码:在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}。提前致谢!

回答

26

该值不会自动为零。该值最初未定义。但是,如果你将它看作一个数字(例如,将它应用于++),那么Perl将它视为零。如果你把它当作一个字符串处理(例如,将.应用于它),那么Perl将它视为一个空字符串。

perldoc perlsyn,在“声明”:

你需要 Perl的声明是报告格式和 子程序(有时甚至没有 子程序)唯一的东西。一个变量包含 未定义的值(“undef”),直到它有 被分配一个定义值,其中 是“undef”以外的任何值。当 用作数字时,“undef”被视为 为0;当作为一个字符串使用时,它被视为空字符串“012”,“ ”。和 当用作不是 被分配的参考时,它将被视为 错误。

4

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

Perl术语中的术语是'autovivified'。

你可能想要做的是使用exists keyword

$res->{ipv6}{pa}{$row->[2]}++ if exists($res->{ipv6}{pa}{$row->[2]}); 
+0

exists关键字测试以查看密钥是否在哈希中,而不是该值是否为undef。 使用Test :: More tests => 4; my%h =('a'); ok(存在$ h {a}); ok(!defined $ h {a}); ok(!exists $ h {b}); ok(!defined $ h {b}); – Axeman 2009-05-01 18:42:34

+0

我知道。关键是他需要了解自动版化和存在但未定义的关键字与未定义关键字之间的区别以及如何处理每个关键字 - 如果他遵循我提供的链接并阅读关于exists关键字的文档。 – 2009-05-02 19:04:17

5

为了详细说明特勒马库斯后,未初始化值将是不确定的。该结构的深部是autovivified。这是一个方便的功能,可自动为您创建数据结构。 Autovivification在你想要的时候非常棒,但是当你想要阻止它的时候会很痛苦。关于理解自动化的知识,网络上有许多教程,文章和文章。

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

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

然后是undef将递增,因为是undef numifies为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

+2

++和 - 运算符不会警告使用未初始化的值。相反,他们默默地将undef转换成0. – 2009-05-01 17:37:04

2

有没有这样的事情作为未初始化的散列密钥。可以未初始化的东西是对于特定的密钥。哈希值只是一个标量值;它与$foo这样的变量没有什么不同。

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

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

最终,您会自动切换到$res->{ipv6}{pa}{$row->[2]},这是未定义的。请记住,这只是一个标量值,如$foo。这种行为与说法相同

my $foo; 
$foo++; 

当你使用未定义的值时,Perl会做特殊的事情。如果将它们用作数字,则Perl会将它们转换为0.如果将它们用作字符串,Perl会将它们转换为“'(空字符串)。在大多数情况下,如果您启用了警告(您应该),您会收到“使用未初始化的值...”警告。但是,auto-increment运营商(++)是一种特殊情况。为了方便起见,在将其递增之前,将其默认值从undef转换为0