Как мне рекурсивно обойти вложенную структуру хэш-данных?

#perl #recursion #hash

#perl #рекурсия #хэш

Вопрос:

Я застрял с тем, что для меня выглядит как простая концептуальная проблема. После тщательного поиска похожих проблем в Интернете и Stack Overflow я не смог найти ничего подобного, поэтому я подумал, что могу спросить вас.

Я создаю структуру данных hash of hash, которая является глубоко вложенной. Глубина может составлять 10-20 раз. Ради этой проблемы я перечисляю только до глубины один.

Я не могу рекурсивно просмотреть приведенный ниже пример хэша в Perl. Я также включил свой код.

Это выдает мне следующую ошибку:

Не удается использовать string («1») в качестве ссылки на ХЭШ, пока «строгие ссылки» используются в

Просто чтобы было понятно: мой хэш обязательно должен иметь определенные ключи со значением 1. Я не могу их избежать.

 $VAR1 = {
    'Eukaryota' => {
        'Rhodophyta'         => {'count' => 5},
        'Alveolata'          => {'count' => 16},
        'stramenopiles'      => {'count' => 57},
        'count'              => 155,
        'Glaucocystophyceae' => {'count' => 1},
        'Cryptophyta'        => {'count' => 18},
        'Malawimonadidae'    => {'count' => 1},
        'Viridiplantae'      => {'count' => 57},
    },
    'Bacteria' => {
        'Cyanobacteria'       => {'count' => 1},
        'Actinobacteria'      => {'count' => 4},
        'count'               => 33,
        'Proteobacteria'      => {'count' => 25},
        'Deinococcus-Thermus' => {'count' => 2},
        'Firmicutes'          => {'count' => 1},
    },
};
  

Код для рекурсивного обхода этого хэша:

 sub analyse_contig_tree_recursively {
    my $TAXA_TREE   = shift @_;
    my $contig_hash = shift @_;
    foreach (keys %{$TAXA_TREE}) {
        print "$_ n";
        analyse_contig_tree_recursively($TAXA_LEVEL->{$_}, $contig_hash);
    }
}
  

Ответ №1:

Я не уверен, что вы вызываете analyse_contig_tree_recursively (вы нигде не используете этот $contig_hash параметр, и вы не определили $TAXA_LEVEL : вы имели в виду $TAXA_TREE ?), Но, очевидно, есть несоответствие между вашим макетом структуры данных и вашим шаблоном рекурсивного обхода. Ваша функция обхода предполагает, что все записи являются хэшами, и обрабатывает пустые хэши как случай завершения: если keys %{$TAXA_TREE} пусто, рекурсивный вызов отсутствует. Учитывая ваши данные, вам нужно проверить, является ли значение хэшем или нет, и не выполнять рекурсию, если вы обнаружите, что это не хэш.

 sub analyse_contig_tree_recursively {
    my $TAXA_TREE           =   shift @_;
    foreach ( keys %{$TAXA_TREE} ){
        print "$_ n";
        if (ref $TAXA_TREE->{$_} eq 'HASH') {
            analyse_contig_tree_recursively($TAXA_TREE->{$_});
        }
    }
}
  

Комментарии:

1. или Scalar::Util::reftype($TAXA_TREE->{$_}) eq 'HASH' , если структура данных может содержать благословенные объекты.

2. @Giles : Большое спасибо. Мне бросилось в глаза, что в моем хэше есть ключи, которые не указывают на ссылку на хэш и могут сигнализировать об окончании рекурсии. О $TAXA_LEVEL, $ contig_hash : это лишь некоторые из других переменных, которые я использую для обработки. Теперь основная проблема ясна, и моя программа работает… Вы, ребята, быстрые и потрясающие.. Большое спасибо

3. Чувак, вы, ребята, быстрые. В случае, если вы просто хотите увидеть свою структуру и не обязательно что-либо с ней делать, используйте Data::Dumper . Кстати, когда вы переходите к хэшам хэшей или спискам хэшей, или спискам хэшей списков и т.д., Самое время начать думать об объектно-ориентированном программировании. Настройка занимает еще несколько минут, но может избавить вас от многих душевных страданий при последующей отладке. Я рекомендую это даже для одноразовых сделок.

4. @David : Спасибо за ваш вклад. Вы говорите о Perl OOPs? Если это так, я хотел бы увидеть пример. Я не могу понять, как я мог бы сделать то же самое, используя ООП в perl. Я должен признать, что я не использую много ООП в perl, кроме Moose, который упростил это.

5. Да, ООП на Perl. Я бы изменил структуру. Сделайте этот класс с именем «family», Каждое семейство содержит два члена: COUNT и MEMBERS, которые представляют собой хэш, содержащий объекты «семейства». Функция-член будет перечислять все ЭЛЕМЕНТЫ. Затем вы могли бы рекурсивно вызвать эту функцию-член. Я работал над этим, когда Джайлс опубликовал ответ.