Этот ответ основан на идее Дэйва Хинтона, а именно на написании подпрограммы общего назначения для обхода хеш-структуры. Такой обработчик хэшей берет ссылку на код и просто вызывает этот код для каждого конечного узла в хэше.
При таком подходе один и тот же хеш-обходчик может использоваться для многих вещей, в зависимости от того, какой обратный вызов мы ему даем. Для еще большей гибкости вам потребуется передать два обратных вызова - один для вызова, когда значение является ссылкой на хеш, а другой для вызова, когда это обычное скалярное значение. Подобные стратегии более подробно рассматриваются в превосходной книге Марка Джейсона Домина, 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);