17

キーの値が他のハッシュであるハッシュがあります。

例:{'key' => {'key2' => {'key3' => 'value'}}}

この構造をどのように繰り返すことができますか?

4

8 に答える 8

24

この回答は、デイブ・ヒントンの背後にあるアイデア、つまり、ハッシュ構造をウォークする汎用サブルーチンを作成するというアイデアに基づいています。このようなハッシュ ウォーカーは、コード参照を取得し、ハッシュ内の各リーフ ノードに対してそのコードを呼び出すだけです。

このようなアプローチでは、与えるコールバックに応じて、同じハッシュ ウォーカーを使用して多くのことを行うことができます。さらに柔軟性を高めるには、値がハッシュ参照の場合に呼び出すコールバックと、通常のスカラー値の場合に呼び出すコールバックの 2 つのコールバックを渡す必要があります。このような戦略については、Marc Jason Dominus の優れた著書Higher Order Perlで詳しく説明されています。

use strict;
use warnings;

sub hash_walk {
    my ($hash, $key_list, $callback) = @_;
    while (my ($k, $v) = each %$hash) {
        # Keep track of the hierarchy of keys, in case
        # our callback needs it.
        push @$key_list, $k;

        if (ref($v) eq 'HASH') {
            # Recurse.
            hash_walk($v, $key_list, $callback);
        }
        else {
            # Otherwise, invoke our callback, passing it
            # the current key and value, along with the
            # full parentage of that key.
            $callback->($k, $v, $key_list);
        }

        pop @$key_list;
    }
}

my %data = (
    a => {
        ab => 1,
        ac => 2,
        ad => {
            ada => 3,
            adb => 4,
            adc => {
                adca => 5,
                adcb => 6,
            },
        },
    },
    b => 7,
    c => {
        ca => 8,
        cb => {
            cba => 9,
            cbb => 10,
        },
    },
);

sub print_keys_and_value {
    my ($k, $v, $key_list) = @_;
    printf "k = %-8s  v = %-4s  key_list = [%s]\n", $k, $v, "@$key_list";
}

hash_walk(\%data, [], \&print_keys_and_value);
于 2010-03-02T14:07:45.337 に答える
12

これは、あなたの望むことですか?(未テスト)

sub for_hash {
    my ($hash, $fn) = @_;
    while (my ($key, $value) = each %$hash) {
        if ('HASH' eq ref $value) {
            for_hash $value, $fn;
        }
        else {
            $fn->($value);
        }
    }
}

my $example = {'key' => {'key2' => {'key3' => 'value'}}};
for_hash $example, sub {
    my ($value) = @_;
    # Do something with $value...
};
于 2010-03-02T13:10:10.233 に答える
7

以前の回答は、独自のソリューションを展開する方法を示しています。これは、perl の参照とデータ構造がどのように機能するかを理解するために、少なくとも 1 回実行することをお勧めします。まだ読んでいない場合は、 perldoc perldscperldoc perlrefを必ず読んでください。

ただし、独自のソリューションを作成する必要はありません。CPAN には、任意に複雑なデータ構造を反復処理するモジュールが既にあります: Data::Visitor

于 2010-03-02T19:20:38.407 に答える
7

この投稿は役に立つかもしれません。

foreach my $key (keys %hash) {
    foreach my $key2 (keys %{ $hash{$key} }) {
        foreach my $key3 (keys %{ $hash{$key}{$key2} }) {
            $value = $hash{$key}{$key2}->{$key3};
            # .
            # .
            # Do something with $value
            # .
            # .
            # .
        }
    }
}
于 2010-03-02T13:09:49.657 に答える
1

これは実際には新しい答えではありませんが、すべてのハッシュ値を再帰的に出力するだけでなく、必要に応じて変更する方法も共有したいと思いました。

これは、値が参照としてコールバックに渡されるため、コールバックルーチンがハッシュ内のすべての値を変更できるように、dave4420 の回答を非常にわずかに変更したものです。

また、各ループが参照ではなくコピーを作成するため、ハッシュを再構築する必要がありました。

sub hash_walk {
   my $self = shift;
    my ($hash, $key_list, $callback) = @_;
    while (my ($k, $v) = each %$hash) {
        # Keep track of the hierarchy of keys, in case
        # our callback needs it.
        push @$key_list, $k;

        if (ref($v) eq 'HASH') {
            # Recurse.
            $self->hash_walk($v, $key_list, $callback);
        }
        else {
            # Otherwise, invoke our callback, passing it
            # the current key and value, along with the
            # full parentage of that key.
            $callback->($k, \$v, $key_list);
        }

        pop @$key_list;
        # Replace old hash values with the new ones
        $hash->{$k} = $v;
    }
}

hash_walk(\%prj, [], \&replace_all_val_strings);

sub replace_all_val_strings {
    my ($k, $v, $key_list) = @_;
    printf "k = %-8s  v = %-4s  key_list = [%s]\n", $k, $$v, "@$key_list";
    $$v =~ s/oldstr/newstr/;
    printf "k = %-8s  v = %-4s  key_list = [%s]\n", $k, $$v, "@$key_list";
}
于 2011-07-29T17:43:05.653 に答える
0
foreach my $keyname (keys(%foo) {
  my $subhash = $foo{$keyname};
  # stuff with $subhash as the value at $keyname
}
于 2010-03-02T12:49:02.390 に答える
0

2回ループする必要があります。すなわち

while ( ($family, $roles) = each %HoH ) {
   print "$family: ";
   while ( ($role, $person) = each %$roles ) {
      print "$role=$person ";
   }
print "\n";
}
于 2010-03-02T12:51:30.873 に答える