为什么Perl认为存在不存在的多级哈希元素?

时间:2011-08-25 00:45:44

标签: perl hash zero

抱歉,这似乎是一个基本问题,但我仍然不明白。如果我有哈希,例如:

my %md_hash = ();
$md_hash{'top'}{'primary'}{'secondary'} = 0;

为什么这是真的?

if ($md_hash{'top'}{'foobar'}{'secondary'} == 0) {
    print "I'm true even though I'm not in that hash\n";
}

哈希中没有“foobar”级别,所以不应该导致false?

TIA

3 个答案:

答案 0 :(得分:7)

您正在测试数字零的未定义值。当然,你会得到真正的结果!你有什么期待?

您还应该在use warnings下收到警告。你为什么不来?

如果您没有使用以下内容启动程序:

use v5.12;  # or whatever it is you are using
use strict;
use warnings;
你真的不应该打扰。 :)


修改

  

注意:我只是澄清了正确性,因为注释行并不好。我真的不太关心一个关于声望点的人。我只想让人们理解。

即使在CPAN autovivification模块下,也没有任何变化。证人:

use v5.10;
use strict;
use warnings;
no  autovivification;

my %md_hash = ();

$md_hash{top}{primary}{secondary} = 0;

if ($md_hash{top}{foobar}{secondary} == 0) {
    say "yup, that was zero.";
}

运行时,说:

$ perl /tmp/demo
Use of uninitialized value in numeric eq (==) at /tmp/demo line 10.
yup, that was zero.

测试是==运算符。其RHS为0。其LHS为undef ,无论自动更新。由于undef在数字上0,这意味着LHS和RHS都包含0==正确地标识为保持相同的数字。

自动生成不是问题,因为当他写“这不是一个多维哈希特定问题”时,ysth正确地观察到了。重要的是你传递给==的内容。 undef在数字上0

如果你真的想要通过使用CPAN编译指示,你可以停止autoviv。但你永远无法通过压制autoviv来改变这里发生的事情。这表明它根本不是一个autoviv问题,只是一个undef

现在,当您执行此操作时, 将获得“额外”键,因为未定义的左值将在通过解除引用链的路上填充。这些必然是完全相同的:

$md_hash{top}{foobar}{secondary}            # implicit arrow for infix deref
$md_hash{top}->{foobar}->{secondary}        # explicit arrow for infix deref
${ ${ $md_hash{top} }{foobar} }{secondary}  # explicit prefix deref

每当你在Perl中deref一个有价值的undef时,该存储位置总是被填入正确类型的对新分配的正确类型的匿名指示对象的引用。简而言之,它是autovivs

并且 你可以通过抑制或者通过回避autoviv来停止。但是,拒绝autoviv与回避它是不一样的,因为你只是改变了返回的东西。整体考验仍然得到充分评估:没有自动短路只是因为你压制了autoviv。那是因为autoviv不是问题(如果它不在那里,你会非常恼火:相信我)。

如果你想要短路,你必须自己写。我似乎永远不需要自己。在Perl中,就是这样。另一方面,C程序员习惯于编写

 if (p && p->whatever) { ... }

所以你也可以这样做,如果你愿意的话。然而,根据我自己的经验,这是非常罕见的。你几乎不得不在Perl中纠错,因为它总是有所作为,因为很容易安排你的代码而不是来改变它在空级别时的行为方式。

答案 1 :(得分:7)

这不是多维哈希特定问题。

一样
my %foo;
if ( $foo{'bar'} == 0 ) {
    print "I'm true even though I'm not in that hash\n";
}

$foo{'bar'}是undef,它将true与0进行比较,尽管如果您已启用警告,则会发出警告。

您的案件还有其他副作用;当你说

my %md_hash = ();
$md_hash{'top'}{'primary'}{'secondary'} = 0;

if ( $md_hash{'top'}{'foobar'}{'secondary'} == 0 ) {
    print "I'm true even though I'm not in that hash\n";
}

$md_hash{'top'}返回哈希引用,并在该哈希中查找“foobar”键。由于{'secondary'},'foobar'元素查找在哈希 - 引用上下文中。这使$md_hash{'top'}{'foobar'}“自动”将哈希引用作为'foobar'键的值,使您具有以下结构:

my %md_hash = (
    'top' => {
        'primary' => {
            'secondary' => 0,
        },
        'foobar' => {},
    },
);

autovivification pragma可用于禁用此行为。人们有时会声称exists()对自动生成有一些影响,但事实并非如此。

答案 2 :(得分:6)

尝试搜索“Perl autovivification”。

首次访问时,哈希值会“弹出”。在这种情况下,值为undef,当解释为数字时为零。

要在不自动生成哈希值的情况下测试是否存在哈希值,请使用exists运算符:

if (exists $md_hash{'top'}{'foobar'}{'secondary'}
    && $md_hash{'top'}{'foobar'}{'secondary'} == 0) {
    print "I exist and I am zero\n";
}

请注意,这仍然会自动生成$md_hash{'top'}和。{ $md_hash{'top'}{'foobar'}(即子哈希)。

[编辑]

正如tchrist在评论中指出的那样,将undef与任何事物进行比较是一种糟糕的风格。因此,编写此代码的更好方法是:

if (defined $md_hash{'top'}{'foobar'}{'secondary'}
    && $md_hash{'top'}{'foobar'}{'secondary'} == 0) {
    print "I exist and I am zero\n";
}

(尽管现在这将自动生成嵌套哈希的所有三个级别,将最低级别设置为undef'。)