Есть ли простой способ проверить хэш элемента hash существует и определен? - PullRequest
5 голосов
/ 27 апреля 2010

Мне нужно проверить хеш-элемент Perl, такой как $Table{$key1}{$key2}, чтобы он существовал и был определен. Вот что я делаю. (Понятия не имею $key1 даже существует)

if 
((defined $Table{$key1}) &&
 (exists  $Table{$key1}) &&
 (defined $Table{$key1}{$key2}) &&
 (exists  $Table{$key1}{$key2})) 
{
   #do whatever
}

Есть ли более простой и чистый способ сделать это?

Ответы [ 5 ]

8 голосов
/ 27 апреля 2010

Вам не нужно проверять каждый уровень иерархии: вы можете просто выбрать значение, которое вам небезразлично. exists не проверяет определенность, только если слот в хэше существует (он может существовать с неопределенным значением), поэтому, если вы заботитесь о том, что значение определено, вам нужно будет вызвать defined, а не существовать. Если значение не определено, оно в логическом контексте оценивается как ложное, поэтому мы можем набрать немного меньше и уменьшить ваш пример до:

if ($Table{$key1}{$key2})
{
   # do whatever
}

Однако, если значение в этом ключе определено, но имеет значение «false» (численно оценивается как ноль или является пустой строкой), это может привести к ложному отрицанию, поэтому мы должны явно проверить на определенность, если это возможно :

if (defined $Table{$key1}{$key2})
{
   # do whatever
}

Если вы не хотите автовивифицировать $Table{$key1}, вы можете сначала проверить его существование, , что приводит нас к «лучшему» способу для общего случая :

if (exists $Table{$key1} and defined $Table{$key1}{$key2})
{
   # do whatever
}

Если вы собираетесь делать это много для различных полей в хэше, вы можете добавить несколько методов доступа в стиле OO, которые бы сделали эту работу за вас:

sub has_field
{
    my ($this, $fieldName) = @_;
    return exists $this->{data} && defined $this->{data}{$fieldName});
}

Я уверен, что вы уже прочитали это, но не помешает снова прочитать соответствующую документацию:

Учитывая выражение, которое задает хеш-элемент или элемент массива, exists возвращает true, если указанный элемент в хеш-коде или массиве когда-либо был инициализирован, даже если соответствующее значение не определено. Элемент не будет автоматически восстановлен, если он не существует.
...
Элемент хеша или массива может быть истинным, только если он определен, и определен, если он существует, но обратное не всегда верно.

6 голосов
/ 27 апреля 2010

Следующее короче и защитит от автовивификации:

 if (exists $table{$key1} and defined $table{$key1}{$key2}) {...}

Другие проверки в вашем коде не нужны.

1 голос
/ 28 апреля 2010

Вы можете проверить Данные :: Diver . Он погружается в структуры данных без автовивизации. Синтаксис будет:

if ( defined Dive(\%Table, $key1, $key2) ) { ... }

или даже:

if ( defined(my $value = Dive(\%Table, $key1, $key2) ) ) {
  ...do something with $value...
}
1 голос
/ 27 апреля 2010

Сначала проверьте существование, затем определите. (Значение может существовать, не будучи определенным, но не может быть определено без существующего.) Вам следует проверить промежуточные уровни с помощью exists, чтобы предотвратить непреднамеренную автовивификацию. Для последнего уровня вам нужно всего лишь позвонить defined. Когда слоев не так много, легко написать код:

if (exists $hash{a} && defined $hash{a}{b}) {...}

Это становится неловко, если есть много слоев:

if (exists $hash{a} && exists $hash{a}{b} && exists $hash{a}{b}{c} ...) {...}

В этом случае вы можете написать версию defined, которая не будет автоматически активировать промежуточные значения:

sub safe_defined {
    my $h = shift;

    foreach my $k (@_) {
        if (ref $h eq ref {}) {
            return unless exists $h->{$k};
            $h = $h->{$k};
        }
        else {
            return;
        }
    }

    return defined $h;
}

Вы используете это так:

if (safe_defined(\%hash, qw(a b c))) {
     say $hash{a}{b}{c};
}

Примечание: эта версия функции ограничена.

  • Он обрабатывает только вложенные хэши. Perl позволяет вам создавать произвольные данные структуры, как хэш массивов скалярных ссылок ...
  • Он не поддерживает благословенные ссылки (т.е. объекты).

Действительно читательская версия оставлена ​​в качестве упражнения. ;)

0 голосов
/ 28 апреля 2010

Отлично! Спасибо всем за ответ.

Так как автовивификация является проблемой для меня, в настоящее время я использую "неловкий" подход, т.е. if (существует $ Table {$ key1} && определено $ Table {$ key1} {$ key2}) {

Делай что угодно

}

Это работает для меня, однако, как вы, ребята, сказали, у меня есть 3-4 уровня глубины вложенного хэша, код немного грязный.

Я проверю Данные: Дайвер. Это выглядит лучше.

Спасибо, еще раз,

...