Perl, проверьте, существует ли пара в хэше хэшей - PullRequest
1 голос
/ 02 декабря 2010

В Perl у меня есть хэш хэшей, созданный с помощью цикла, подобного следующему

    my %HoH
    for my $i (1..10) {
      $HoH{$a}{$b} = $i; 
    }

$ a и $ b - это переменные, которые имеют некоторое значение при заполнении HoH. После создания HoH, как я могу проверить, существует ли конкретная пара ($ c, $ d) в HoH? Следующее не работает

if (defined $HoH{$c}{$d}) {...}

потому что, если $ c еще не существует в HoH, он будет создан как ключ без значения.

Ответы [ 5 ]

4 голосов
/ 02 декабря 2010

Запись

if (defined $HoH{$c}{$d}) {...}

будет «работать» настолько, что сообщит вам, имеет ли $HoH{$c}{$d} определенное значение. Проблема в том, что если $HoH{$c} еще не существует, он будет создан (с соответствующим значением), чтобы можно было проверить $HoH{$c}{$d}. Этот процесс называется "автовивификацией". Это удобно при установке значений, например

my %hoh;
$hoh{a}{b} = 1; # Don't need to set '$hoh{a} = {}' first

но неудобно при извлечении возможно несуществующих значений. Мне бы хотелось, чтобы Perl был достаточно умен, чтобы выполнять автовивификацию только для выражений, используемых как lvalues ​​и short-circuit, чтобы возвращать undef для rvalues, но, увы, это не так волшебно. Прагма autovivification (доступная в CPAN) добавляет функциональность для этого.

Чтобы избежать автовивификации, сначала нужно проверить промежуточные значения:

if (exists $HoH{$c} && defined $HoH{$c}{$d}) {
     ...
}
3 голосов
/ 02 декабря 2010
use Data::Dumper;

my %HoH;

$HoH{A}{B} = 1;

if(exists $HoH{C} && exists $HoH{C}{D}) {
   print "exists\n";
}

print Dumper(\%HoH);

if(exists $HoH{C}{D}) {
   print "exists\n";
}

print Dumper(\%HoH);

Выход:

$VAR1 = {
          'A' => {
                   'B' => 1
                 }
        };
$VAR1 = {
          'A' => {
                   'B' => 1
                 },
          'C' => {}
        };

Автовификация вызывает создание ключей. «существует» во втором примере показывает это, поэтому первый пример проверяет оба ключа по отдельности.

2 голосов
/ 02 декабря 2010

Несколько способов:

if ( $HoH{$c} && defined $HoH{$c}{$d} ) {...}

или

if ( defined ${ $HoH{$c} || {} }{$d} ) {...}

или

no autovivification;
if (defined $HoH{$c}{$d}) {...}

или

use Data::Diver;
if ( defined Data::Diver::Dive( \%HoH, $c, $d ) ) {...}
0 голосов
/ 02 декабря 2010

Мой дубль:

use List::Util   qw<first>;
use Params::Util qw<_HASH>;

sub exists_deep (\[%$]@) {
    my $ref = shift;
    return unless my $h = _HASH( $ref ) // _HASH( $$ref )
              and defined( my $last_key = pop )
              ;
    # Note that this *must* be a hash ref, for anything else to make sense.
    return if first { !( $h = _HASH( $h->{ $_ } )) } @_;
    return exists $h->{ $last_key };
}

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

0 голосов
/ 02 декабря 2010

Вы должны использовать функцию существующие

существующие EXPR

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

Обратите внимание, что EXPR может быть произвольно усложнен, если последняя операция - поиск ключа хеша или массива или имя подпрограммы:

  1. if (существует $ ref -> {A} -> {B} -> {$ key}) {}
  2. if (существует $ hash {A} {B} {$ key}) {}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...