为什么Perl认为存在不存在的多级散列元素?

发布于 2024-12-01 10:22:29 字数 332 浏览 0 评论 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";
}

哈希中没有“foobar”级别,所以这不会导致错误吗?

TIA

Sorry, this seems like such a basic question but I still don't understand. If I have a hash, for example:

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

How come this is true?

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

There is no "foobar" level in the hash so shouldn't that result in false?

TIA

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

原来分手还会想你 2024-12-08 10:22:29

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

它的工作原理与 $foo{'bar'} is undef 相同

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

,它将 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()对自动视觉化有一些影响,但事实并非如此。

This isn't a multidimensional hash specific question.

It works the same with

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

$foo{'bar'} is undef, which compares true to 0, albeit with a warning if you have warnings enabled as you should.

There is an additional side effect in your case; when you say

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'} returns a hash reference, and the 'foobar' key is looked for in that hash. Because of the {'secondary'}, that 'foobar' element lookup is in hash-dereference context. This makes $md_hash{'top'}{'foobar'} "autovivify" a hash reference as the value of the 'foobar' key, leaving you with this structure:

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

The autovivification pragma can be used to disable this behavior. People sometimes assert that exists() has some effect on autovifification, but this is not true.

九八野马 2024-12-08 10:22:29

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

您还应该在使用警告下收到警告。你为什么不呢?

如果你不开始一个程序:

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 时,该存储位置总是被填充为对新分配的匿名引用的正确类型的引用正确的类型。简而言之,它会自动生存

你可以通过抑制或回避自动生存来停止。然而,拒绝 autoviv 与回避它并不相同,因为你只是改变了返回的内容。整体表达仍然得到充分评估:不存在自动短路仅仅因为你抑制了autoviv。那是因为 autoviv 不是问题(如果它不存在,你会真的很生气:相信我)。

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

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

And,所以如果您愿意,您也可以这样做。然而,以我自己的经验来看,这种情况相当罕见。在 Perl 中,您几乎必须竭尽全力才能发挥作用,因为很容易安排您的代码在存在空级别时改变其行为方式。

You are testing an undefined value for numeric zero. Of course you get a true result! What were you expecting?

You should also get a warning under use warnings. Why didn’t you?

If you do not start a program with:

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

you really shouldn’t even bother. :)


EDIT

NB: I am only clarifying for correctness, because the comment lines are not good for that. I really could not possibly care one whingeing whit less about the reputation points. I just want people to understand.

Even under the CPAN autovivification module, nothing changes. Witness:

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.";
}

When run, that says:

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

The test is the == operator. Its RHS is 0. Its LHS is undef irrespective of autovivification. Since undef is numerically 0, that means that both LHS and RHS contain 0, which the == correctly identifies as holding the same number.

Autovivification is not the issue here, as ysth correctly observes when he writes that “This isn’t a multidimensional hash specific question.” All that matters is what you pass to ==. And undef is numerically 0.

You can stop autoviv if you really, really want to — by using the CPAN pragma. But you will not ever manage to change what happens here by suppressing autoviv. That shows that it is not an autoviv matter at all, just an undef one.

Now, you will get “extra” keys when you do this, since the undefined lvalue will get filled in on the way through the dereference chain. These are neccesarily all the same:

$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

Whenever you deref an lvaluable undef in Perl, that storage location always gets filled in with the proper sort of reference to a newly allocated anonymous referent of the proper type. In short, it autovivs.

And that you can stop either by suppressing or else by sidestepping the autoviv. However, denying the autoviv is not the same as sidestepping it, because you just change what sort of thing gets returned. The overall expession is still fully evaluated: there is no automatic short-circuiting just because you suppress autoviv. That’s because autoviv is not the problem (and if it were not there, you would be really annoyed: trust me).

If you want short-circuiting, you have to write that yourself. I never seem to need to myself. In Perl, that is. On the other hand, C programmers are quite accustomed to writing

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

And so you can do that, too, if you want to. However, it is pretty rare in my own experience. You almost have to bend over wrongwards in Perl for that ever to make a difference, as it is quite easy to arrange for your code not to change how it acts if there are empty levels.

烟凡古楼 2024-12-08 10:22:29

尝试搜索“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'。)

Try a search on "Perl autovivification".

The hash values "spring into existence" when you first access them. In this case, the value is undef, which when interpreted as a number is zero.

To test for existence of a hash value without auto-vivifying it, use the exists operator:

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

Note that this will still auto-vivify $md_hash{'top'} and
$md_hash{'top'}{'foobar'} (i.e. the sub-hashes).

[edit]

As tchrist points out in a comment, it is poor style to compare undef against anything. So a better way to write this code would be:

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

(Although this will now auto-vivify all three levels of the nested hash, setting the lowest level to undef'.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文