6

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

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”级别,所以不应该导致错误吗?

TIA

4

3 回答 3

7

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

您还应该在use warnings. 你为什么不呢?

如果您不使用以下方式启动程序:

use v5.12;  # or whatever it is you are using
use strict;
use warnings;

你真的不应该打扰。:)


编辑

注意:我只是为了澄清正确性,因为注释行对此不利。我真的不可能不在乎一个关于声誉点的抱怨。我只是想让人们理解。

即使在 CPANautovivification模块下,也没有任何变化。见证:

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. 它的 LHSundef 与 autovivification 无关。由于undef是 数字0,这意味着 LHS 和 RHS 都包含0==正确识别为持有相同的数字。

自动生存不是这里的问题,正如 ysth 在他写道“这不是一个多维散列特定问题”时正确观察到的那样。重要的是你传递给什么==。并且undef在数字上是0

如果您真的非常想停止 autoviv,可以使用 CPAN pragma。但是你永远无法通过抑制 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

每当您undef在 Perl 中取消引用一个左值时,该存储位置总是会填充有对新分配的正确类型的匿名引用的正确类型的引用。简而言之,它 autovivs

而且可以通过抑制或回避自动激活来停止。然而,否认 autoviv 与回避它并不相同,因为您只需更改返回的内容类型。整体表现仍然得到充分评估:不会因为您抑制 autoviv 而自动短路。那是因为 autoviv 不是问题(如果它不存在,你会很生气:相信我)。

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

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

因此,如果您愿意,您也可以这样做。但是,在我自己的经验中,这种情况很少见。你几乎不得不在 Perl 中犯错才能有所作为,因为如果有空级别,很容易安排你的代码改变它的行为方式。

于 2011-08-25T01:02:17.030 回答
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'}“autovivify”作为 'foobar' 键的值的哈希引用,留下了这个结构:

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

autovivification pragma 可用于禁用此行为。人们有时断言 exists() 对自动验证有一些影响,但事实并非如此。

于 2011-08-25T01:03:23.743 回答
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'。)

于 2011-08-25T00:50:35.167 回答