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

Ana*_*log 5 perl hash zero

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

my %md_hash = ();
$md_hash{'top'}{'primary'}{'secondary'} = 0;
Run Code Online (Sandbox Code Playgroud)

怎么回事呢?

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

哈希中没有"foobar"级别,所以不应该导致错误?

TIA

tch*_*ist 7

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

您还应该收到警告use warnings.你为什么不呢?

如果您没有启动程序:

use v5.12;  # or whatever it is you are using
use strict;
use warnings;
Run Code Online (Sandbox Code Playgroud)

你真的不应该打扰.:)


编辑

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

即使在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.";
}
Run Code Online (Sandbox Code Playgroud)

运行时,说:

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

测试是==操作员.它的RHS是0.它的LHS undef 与自动化无关.因为undef在数字上0,这意味着LHS和RHS都包含0,==正确地标识为保持相同的数字.

自动生成不是问题所在,因为当他写下"这不是一个多维哈希特定问题"时,正确地观察到了.所有重要的是你传递给的东西==.并且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
Run Code Online (Sandbox Code Playgroud)

每当你undef在Perl中deref一个lvaluable 时,该存储位置总是被适当类型的引用填充到新分配的正确类型的匿名引用对象.简而言之,它是autovivs.

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

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

 if (p && p->whatever) { ... }
Run Code Online (Sandbox Code Playgroud)

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

  • @ysth:我从未离开过p5p.我只是跳过阅读它好几年了.我仍然在我的邮件分区中拥有整个内容.:) (2认同)

yst*_*sth 7

这不是一个多维哈希特定问题.

它的工作原理与之相同

my %foo;
if ( $foo{'bar'} == 0 ) {
    print "I'm true even though I'm not in that hash\n";
}
Run Code Online (Sandbox Code Playgroud)

$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";
}
Run Code Online (Sandbox Code Playgroud)

$md_hash{'top'}返回哈希引用,并在该哈希中查找"foobar"键.因为{'secondary'},'foobar'元素查找在hash-dereference上下文中.这使得$md_hash{'top'}{'foobar'}"autovivify"哈希引用为'foobar'键的值,使您具有以下结构:

my %md_hash = (
    'top' => {
        'primary' => {
            'secondary' => 0,
        },
        'foobar' => {},
    },
);
Run Code Online (Sandbox Code Playgroud)

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


Nem*_*emo 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";
}
Run Code Online (Sandbox Code Playgroud)

请注意,这仍然会自动生成$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";
}
Run Code Online (Sandbox Code Playgroud)

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