【问题标题】:Does an uninitialized hash key have a default value of zero in Perl?Perl 中未初始化的哈希键的默认值是否为零?
【发布时间】:2010-10-23 03:05:34
【问题描述】:

我有类似于以下的 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}。提前致谢!

【问题讨论】:

    标签: perl hash default-value


    【解决方案1】:

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

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

    您需要在其中声明的唯一内容 Perl 是报告格式和 子程序(有时甚至没有 子程序)。一个变量持有 未定义的值(“undef”),直到它有 被分配了一个定义的值,它 是“undef”以外的任何内容。什么时候 用作数字,“undef”被处理 为 0;当用作字符串时,它是 被视为空字符串,“”;和 当用作参考时 被分配到,它被视为 错误。

    【讨论】:

      【解决方案2】:

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

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

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

      然后undef会自增,因为undef num化为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 中找到警告层次结构。

      【讨论】:

      • ++ 和 -- 运算符不会就使用未初始化的值发出警告。相反,他们默默地将 undef 转换为 0。
      • 实际上,我记得看到 daotoad 描述的相同行为,即在自动增量时收到“未初始化值”警告。你们谁知道发生这种情况的最新 Perl 版本是什么?
      • @j_random - 我刚刚用 Perl 5.8 进行了测试,Michael Carman 是正确的。 perl -w -e '$foo{bar}++' 不会警告未初始化的值。 perl -w -e '$foo{bar} += 2' 也没有。 .=、'-=` 和 '--' 也可以。但是,其他 X= 运算符会生成警告。
      • 谢谢!它甚至可能是一些奇怪的东西,比如$foo{bar}++ 工作正常,但不是++$foo{bar}。我希望我能回忆起确切的情况......
      【解决方案3】:

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

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

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

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

      【讨论】:

      • exists 关键字测试查看键是否在哈希中,而不是值是否为 undef。使用 Test::More 测试 => 4;我的 %h = ( 'a' );好的(存在 $h{a} );好的(!定义的$h{a});好的(!存在 $h{b} );好的( !defined $h{b} );
      • 我知道。关键是他需要了解自动激活以及存在但未定义的键和未定义的键之间的区别以及如何处理每个键 - 如果他遵循我提供的链接并阅读所有这些,他将理解存在关键字的文档。
      【解决方案4】:

      没有未初始化的散列。可以未初始化的是特定键的 value。哈希值只是一个标量值;它与$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

      【讨论】:

      • 非常正确,我实际上指的是特定键的未初始化。 :) 也感谢您的解释
      猜你喜欢
      • 1970-01-01
      • 2014-12-29
      • 2016-10-24
      • 2021-11-11
      • 1970-01-01
      • 1970-01-01
      • 2016-02-01
      • 2014-07-18
      • 1970-01-01
      相关资源
      最近更新 更多