如何将嵌套的Perl哈希干净地转换为非嵌套的?

kno*_*orv 7 perl

假设嵌套的哈希结构%old_hash..

my %old_hash;
$old_hash{"foo"}{"bar"}{"zonk"} = "hello";
Run Code Online (Sandbox Code Playgroud)

..我们想要"扁平化"(对不起,如果这是错误的术语!)使用sub的非嵌套哈希,&flatten(...)以便...

my %h = &flatten(\%old_hash);
die unless($h{"zonk"} eq "hello");
Run Code Online (Sandbox Code Playgroud)

以下定义&flatten(...)的诀窍是:

sub flatten {
  my $hashref = shift;
  my %hash;
  my %i = %{$hashref};
  foreach my $ii (keys(%i)) {
    my %j = %{$i{$ii}};
    foreach my $jj (keys(%j)) {
      my %k = %{$j{$jj}};
      foreach my $kk (keys(%k)) {
        my $value = $k{$kk};
        $hash{$kk} = $value;
      }
    }
  }
  return %hash;
}
Run Code Online (Sandbox Code Playgroud)

虽然给出的代码有效但它不是非常易读或干净.

我的问题是双重的:

  • 给定代码在哪些方面与现代Perl最佳实践不对应?要严厉!:-)
  • 你会如何清理它?

rjh*_*rjh 10

您的方法不是最佳实践,因为它不能扩展.如果嵌套哈希是六个,十个级别深度怎么办?重复应该告诉你,递归例程可能就是你需要的.

sub flatten {
    my ($in, $out) = @_;
    for my $key (keys %$in) {
        my $value = $in->{$key};
        if ( defined $value && ref $value eq 'HASH' ) {
            flatten($value, $out);
        }
        else {
            $out->{$key} = $value;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,良好的现代Perl风格是尽可能使用CPAN.Data :: Traverse可以满足您的需求:

use Data::Traverse;
sub flatten {
    my %hash = @_;
    my %flattened;
    traverse { $flattened{$a} = $b } \%hash;
    return %flattened;
}
Run Code Online (Sandbox Code Playgroud)

最后要注意的是,通过引用传递哈希值通常更有效,以避免它们扩展到列表中然后再次变为哈希值.