Существует ли простой способ проверить хеш элемента хеша, существует и определяется?

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

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

Существует ли более легкий и более чистый способ сделать это?

5
задан Eric Strom 27 April 2010 в 18:45
поделиться

5 ответов

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

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

Это становится неудобно, если слоев много:

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

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

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 позволяет создавать произвольные структуры данных , такие как хэш массивов скалярных ссылок ...
  • Он не поддерживает благословенные ссылки (то есть объекты).

Настоящая общая версия оставлена ​​читателю в качестве упражнения. ;)

1
ответ дан 18 December 2019 в 10:42
поделиться

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

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

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

6
ответ дан 18 December 2019 в 10:42
поделиться

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

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

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

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

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

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

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

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

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

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

8
ответ дан 18 December 2019 в 10:42
поделиться

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

Поскольку автоживирование - проблема для меня, в настоящее время я использую «неудобный» подход, то есть if (существует $ Table {$ key1} && defined $ Table {$ key1} {$ key2}) {

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

}

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

Я проверю Data: Diver. Этот выглядит лучше.

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

0
ответ дан 18 December 2019 в 10:42
поделиться

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

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

или даже:

if ( defined(my $value = Dive(\%Table, $key1, $key2) ) ) {
  ...do something with $value...
}
1
ответ дан 18 December 2019 в 10:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: