Почему Perl считает, что существует несуществующий многоуровневый хеш-элемент? - PullRequest
5 голосов
/ 25 августа 2011

Извините, это похоже на такой основной вопрос, но я все еще не понимаю.Если у меня есть хеш, например:

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 ]

7 голосов
/ 25 августа 2011

Вы тестируете неопределенное значение для числового нуля.Конечно, вы получите истинный результат!Чего ты ожидал?

Вы также должны получить предупреждение под 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.Это показывает, что это не вопрос автовива, а всего лишь undef.

Теперь вы получите дополнительные «ключи» при этом, так как неопределенное значение lvalue будетбыть заполненным на пути через цепочку разыменования.Они обязательно должны быть одинаковыми:

$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

Всякий раз, когда вы переоцениваете lvaluable undef в Perl, это хранилище всегда заполняется надлежащим видом ссылки на недавно выделенный объект.анонимный референт соответствующего типа.Короче говоря, это autovivs .

и , что вы можете остановить, подавляя или же обходя autoviv.Однако отрицание autoviv - это не то же самое, что обходить его стороной, потому что вы просто изменяете, что возвращается.Общее выражение по-прежнему полностью оценивается: нет автоматического короткого замыкания только потому, что вы подавляете autoviv.Это потому, что с autoviv нет проблем (и если бы его там не было, вы бы очень раздражались: поверьте мне).

Если вы хотите замкнуть накоротко, вы должны написать это сами.Я никогда не нуждаюсь в себе.В Perl это так.С другой стороны, программисты на С довольно привыкли писать

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

И поэтому вы тоже можете это делать, если хотите.Однако, по моему собственному опыту, это довольно редко.Вы почти должны ошибаться в Perl, чтобы это когда-либо имело значение, поскольку ваш код не довольно легко изменить, чтобы он действовал, если есть пустые уровни.

7 голосов
/ 25 августа 2011

Этот вопрос не относится к многомерному хешу.

Он работает так же, как

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 может быть использована для отключения этого поведения.Люди иногда утверждают, что exist () как-то влияет на автовификацию, но это не так.

6 голосов
/ 25 августа 2011

Попробуйте выполнить поиск по "Автовивификации Perl".

Хэш-значения "возникают" при первом обращении к ним.В этом случае это значение равно 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'} (т. Е. Вспомогательные хеши).

[править]

Как указывает Трист вкомментарий, это плохой стиль для сравнения undef с чем угодно.Поэтому лучший способ написания этого кода был бы:

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

(хотя теперь это автоматически оживит все три уровня вложенного хэша, установив самый низкий уровень undef '.)

...