キーの値が他のハッシュであるハッシュがあります。
例:{'key' => {'key2' => {'key3' => 'value'}}}
この構造をどのように繰り返すことができますか?
これは、あなたの望むことですか? (未テスト)
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...
};
この答えは、Dave Hintonの背後にある考え方、つまり、ハッシュ構造をウォークするための汎用サブルーチンを作成するという考え方に基づいています。このようなハッシュウォーカーはコード参照を受け取り、ハッシュ内のリーフノードごとにそのコードを呼び出すだけです。
このようなアプローチでは、どのコールバックを与えるかに応じて、同じハッシュウォーカーを使用して多くのことを実行できます。さらに柔軟性を高めるには、2つのコールバックを渡す必要があります。1つは値がハッシュ参照の場合に呼び出すもので、もう1つは通常のスカラー値の場合に呼び出すものです。このような戦略は、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);
以前の回答は、独自のソリューションをロールする方法を示しています。これは、Perl参照とデータ構造がどのように機能するかを理解するために、少なくとも1回は実行することをお勧めします。 perldoc perldsc および perldoc perlref をまだ読んでいない場合は、必ず読んでください。
ただし、独自のソリューションを作成する必要はありません。CPANには、任意に複雑なデータ構造を反復処理するモジュールがすでに存在します。 Data :: Visitor 。
この投稿 役に立つかもしれません。
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
# .
# .
# .
}
}
}
また、 perldoc perldsc をお読みください。ハッシュについて詳しく知ることができます
これは実際には新しい答えではありませんが、すべてのハッシュ値を再帰的に出力するだけでなく、必要に応じて変更する方法を共有したいと思いました。
これは、dave4420の回答を少し変更したもので、値が参照としてコールバックに渡されるため、コールバックルーチンがハッシュ内のすべての値を変更できます。
また、while各ループが参照ではなくコピーを作成するため、ハッシュを再構築する必要がありました。
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";
}
foreach my $keyname (keys(%foo) {
my $subhash = $foo{$keyname};
# stuff with $subhash as the value at $keyname
}
2回ループする必要があります。つまり.
while ( ($family, $roles) = each %HoH ) {
print "$family: ";
while ( ($role, $person) = each %$roles ) {
print "$role=$person ";
}
print "\n";
}
Perlを「CPANインタープリター」として使用している場合は、 _Data::Visitor
_ および _Data::Deep
_ に加えて、非常に単純な _Data::Traverse
_ :
_use Data::Traverse qw(traverse);
my %test_hash = (
q => [qw/1 2 3 4/],
w => [qw/4 6 5 7/],
e => ["8"],
r => {
r => "9" ,
t => "10" ,
y => "11" ,
} ,
);
traverse { next if /ARRAY/; print "$a => $b\n" if /HASH/ && $b > 8 } \%test_hash;
_
出力:
_t => 10
y => 11
_
_$a
_と_$b
_は、sort()
関数内では、ここでは(traverse()
と同様に)特別な変数として扱われます。 _Data::Traverse
_は非常にシンプルですが、CORE以外の依存関係がない非常に便利なモジュールです。