Nano Hash - криптовалюты, майнинг, программирование

Обход многомерного хеша в Perl

Если у вас есть хэш (или ссылка на хеш) в Perl с множеством измерений, и вы хотите выполнить итерацию по всем значениям, как лучше всего это сделать. Другими словами, если у нас есть $f->{$x}{$y}, я хочу что-то вроде

foreach ($x, $y) (deep_keys %{$f})
{
}

вместо

foreach $x (keys %f) 
    {
    foreach $y (keys %{$f->{$x}) 
    {
    }
}

  • Контекст списка не будет возвращать сегментированный список, а сглаженный список, поэтому вы не получите два скаляра одновременно. Лучше, чтобы deep_keys возвращал ссылку на массив. 02.10.2008

Ответы:


1

Вот вариант. Это работает для произвольно глубоких хэшей:

sub deep_keys_foreach
{
    my ($hashref, $code, $args) = @_;

    while (my ($k, $v) = each(%$hashref)) {
        my @newargs = defined($args) ? @$args : ();
        push(@newargs, $k);
        if (ref($v) eq 'HASH') {
            deep_keys_foreach($v, $code, \@newargs);
        }
        else {
            $code->(@newargs);
        }
    }
}

deep_keys_foreach($f, sub {
    my ($k1, $k2) = @_;
    print "inside deep_keys, k1=$k1, k2=$k2\n";
});
02.10.2008
  • Мне это нравится! Спасибо за краткий и работоспособный ответ, который работает из коробки. 10.10.2008

  • 2

    Этап первый: не изобретайте велосипед :)

    Быстрый поиск по CPAN выдает невероятно полезный Data::Walk. Определите подпрограмму для обработки каждого узла, и все готово.

    use Data::Walk;
    
    my $data = { # some complex hash/array mess };
    
    sub process {
       print "current node $_\n";
    }
    
    walk \&process, $data;
    

    И Боб твой дядя. Обратите внимание, что если вы хотите передать ему хэш для обхода, вам нужно передать ссылку на него (см. perldoc perlref) следующим образом (иначе он также попытается обработать ваши хэш-ключи!):

    walk \&process, \%hash;
    

    Для более комплексного решения (которое на первый взгляд сложнее найти в CPAN) используйте Data::Visitor::Callback или его родительский модуль — это дает вам более точный контроль над тем, что вы делаете, и (просто для дополнительной уличной репутации) написан с использованием Moose.

    02.10.2008
  • У меня та же проблема с этим модулем, что и с File::Find: ... функция &wanted является общим обратным вызовом и не сообщает Data::Walk, является ли элемент «разыскиваемым» или нет. Фактически, его возвращаемое значение игнорируется. Почему бы не позволить мне решить, стоит ли спускаться по хэшу? 03.10.2008
  • Вы можете отфильтровать список вещей для обработки с помощью параметра preprocess =› sub {} arg — дополнительные сведения см. в документации. 03.10.2008

  • 3

    Мне кажется, что Data::Diver или Data::Visitor — хороший подход для вас.

    02.10.2008

    4

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

    Как только вы это осознаете, все становится легко. Например:

    sub f($) {
      my $x = shift;
      if( ref $x eq 'HASH' ) {
        foreach( values %$x ) {
          f($_);
        }
      } elsif( ref $x eq 'ARRAY' ) {
        foreach( @$x ) {
          f($_);
        }
      }
    }
    

    Добавьте все, что нужно сделать, кроме обхода структуры, конечно.

    Один отличный способ сделать то, что вам нужно, — передать ссылку на код, который будет вызываться изнутри f. Используя подпрототипирование, вы даже можете сделать вызовы похожими на функции Perl grep и map.

    01.10.2008

    5

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

    $arr{"foo",1} = "one";
    $arr{"bar",2} = "two";
    
    while(($key, $value) = each(%arr))
    {
        @keyValues = split($;, $key);
        print "key = [", join(",", @keyValues), "] : value = [", $value, "]\n";
    }
    

    При этом используется разделитель нижнего индекса "$;" в качестве разделителя для нескольких значений в ключе.

    02.10.2008

    6

    Невозможно получить описанную вами семантику, потому что foreach перебирает список по одному элементу за раз. Вместо этого вам нужно, чтобы deep_keys возвращал LoL (список списков). Даже это не работает в общем случае произвольной структуры данных. Могут быть разные уровни подхэшей, некоторые из уровней могут быть ссылками на МАССИВ и т. д.

    На языке Perlish это можно было бы сделать, написав функцию, которая может проходить по произвольной структуре данных и применять обратный вызов для каждого «листа» (то есть значения, не являющегося ссылкой). ответ bmdhacks является отправной точкой. Точная функция будет варьироваться в зависимости от того, что вы хотите сделать на каждом уровне. Это довольно просто, если все, что вам нужно, это значения листьев. Все усложняется, если вы заботитесь о ключах, индексах и т. д., которые привели вас к листу.

    02.10.2008

    7

    Это достаточно просто, если все, что вы хотите сделать, это работать со значениями, но если вы хотите работать с ключами, вам нужны спецификации того, как уровни будут восстанавливаться.

    а. Например, вы можете указать ключи как "$level1_key.$level2_key.$level3_key" или любой другой разделитель, представляющий уровни.

    б. Или у вас может быть список ключей.

    Я рекомендую последнее.

    • Уровень можно понять по @$key_stack

    • и самый локальный ключ - $key_stack->[-1].

    • Путь может быть реконструирован: join( '.', @$key\_stack )

    Код:

    use constant EMPTY_ARRAY => [];
    use strict;    
    use Scalar::Util qw<reftype>;
    
    sub deep_keys (\%) { 
        sub deeper_keys { 
            my ( $key_ref, $hash_ref ) = @_;
            return [ $key_ref, $hash_ref ] if reftype( $hash_ref ) ne 'HASH';
            my @results;
    
            while ( my ( $key, $value ) = each %$hash_ref ) { 
                my $k = [ @{ $key_ref || EMPTY_ARRAY }, $key ];
                push @results, deeper_keys( $k, $value );
            }
            return @results;
        }
    
        return deeper_keys( undef, shift );
    }
    
    foreach my $kv_pair ( deep_keys %$f ) { 
        my ( $key_stack, $value ) = @_;
        ...
    }
    

    Это было протестировано в Perl 5.10.

    01.10.2008

    8

    Если вы работаете с древовидными данными, уходящим более чем на два уровня в глубину, и вы хотите пройтись по этому дереву, вам следует сначала подумать, что вы собираетесь сделать много дополнительной работы для себя, если вы планируете повторно реализовать все, что вам нужно. делать вручную хэши хэшей хэшей, когда доступно много хороших альтернатив (search CPAN для "Дерево").

    Не зная, каковы на самом деле ваши требования к данным, я слепо укажу вам на руководство по Tree: :DAG_Node для начала.

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

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    my %hash = (
        "toplevel-1" => 
        { 
            "sublevel1a"  => "value-1a",
            "sublevel1b"  => "value-1b"
        },
        "toplevel-2" =>
        {
            "sublevel1c" => 
            {
                "value-1c.1" => "replacement-1c.1",
                "value-1c.2" => "replacement-1c.2"
            },
            "sublevel1d" => "value-1d"
        }
    );
    
    hashwalk( \%hash );
    
    sub hashwalk
    {
        my ($element) = @_;
        if( ref($element) =~ /HASH/ )
        {
            foreach my $key (keys %$element)
            {
                print $key," => \n";
                hashwalk($$element{$key});
            }
        }
        else
        {
            print $element,"\n";
        }
    }
    

    Он выведет:

    toplevel-2 => 
    sublevel1d => 
    value-1d
    sublevel1c => 
    value-1c.2 => 
    replacement-1c.2
    value-1c.1 => 
    replacement-1c.1
    toplevel-1 => 
    sublevel1a => 
    value-1a
    sublevel1b => 
    value-1b
    

    Обратите внимание, что вы НЕ МОЖЕТЕ предсказать, в каком порядке будут проходиться элементы хэша, если вы не привяжете хэш через Tie::IxHash или подобное — опять же, если вы собираетесь выполнить такой большой объем работы, я рекомендую модуль дерева.

    02.10.2008
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..