【问题标题】:Perl: How to turn array into nested hash keysPerl:如何将数组转换为嵌套的哈希键
【发布时间】:2012-07-15 08:20:18
【问题描述】:

我需要将一个扁平的键列表转换为嵌套哈希,如下所示:

我的 $hash = {};

我的@array = qw(key1 key2 lastKey Value);

ToNestedHash($hash, @array);

会这样做:

$hash{'key1'}{'key2'}{'lastKey'} = "值";

【问题讨论】:

    标签: perl perl-data-structures


    【解决方案1】:
    sub to_nested_hash {
        my $ref   = \shift;  
        my $h     = $$ref;
        my $value = pop;
        $ref      = \$$ref->{ $_ } foreach @_;
        $$ref     = $value;
        return $h;
    }
    

    解释:

    • 将第一个值作为 hashref
    • 取最后一个值作为要赋值的值
    • 其余的是键。
    • 然后创建一个 SCALAR 对基本哈希的引用。
    • 反复:
      • 取消引用指针以获取哈希(第一次)或将指针自动激活为哈希
      • 获取密钥的哈希槽
      • 并将标量引用分配给哈希槽。
      • (下次会自动激活到指定的哈希值)。
    • 最后,参考最里面的槽,赋值。

    我们知道:

    • 散列或数组的占用者只能是标量或引用。
    • 引用是一种标量。 (my $h = {}; my $a = [];)。
    • 因此,\$h->{ $key } 是对堆上标量槽的引用,可能是自动激活的。
    • 嵌套散列的“级别”可以自动激活为散列引用如果我们这样处理它。

    这样做可能更明确:

    foreach my $key ( @_ ) { 
        my $lvl = $$ref = {};
        $ref    = \$lvl->{ $key };
    }
    

    但由于重复使用这些参考成语,我完全照原样写了那行,并在发布前对其进行了测试,没有错误。

    至于替代方案,以下版本“更容易”(想出来)

    sub to_nested_hash {
        $_[0] //= {};
        my $h     = shift;
        my $value = pop;
        eval '$h'.(join '', map "->{\$_[$i]}", 0..$#_).' = $value';
        return $h;
    }
    

    但要慢大约 6-7 倍。

    【讨论】:

    • 你能补充解释吗?
    • +1 我不认为通过引用它来自动激活哈希元素
    • @Borodin,当我发现它可以工作时,这是一个上帝赐予。我正在使用一些多级哈希转换来进行一些提要处理,它使我最常用的例程加快了 600%-1000%。
    • 做了一个小改动,所以my $h; to_nested_hash($h, @keys, $val); 可以工作。
    • 进行了小改动,使foo barsystem("rm -rf /") 等键能够正常工作。
    【解决方案2】:

    我认为这段代码更好——更适合移动到类方法中,并且可以根据提供的参数设置一个值。否则选择的答案很整洁。

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    use YAML;
    
    my $hash = {};
    
    my @array = qw(key1 key2 lastKey);
    my $val = [qw/some arbitrary data/];
    
    print Dump to_nested_hash($hash, \@array, $val);
    print Dump to_nested_hash($hash, \@array);
    sub to_nested_hash {
        my ($hash, $array, $val) = @_;
        my $ref   = \$hash;
        my @path = @$array;
        print "ref: $ref\n";
        my $h     = $$ref;
        $ref      = \$$ref->{ $_ } foreach @path;
        $$ref     = $val if $val;
        return $h;
    }
    

    【讨论】:

      【解决方案3】:

      谢谢你的好东西!!!

      我用递归的方式做到了:

      sub Hash2Array
      {
        my $this = shift;
        my $hash = shift;
      
        my @array;
        foreach my $k(sort keys %$hash)
        {
          my $v = $hash->{$k};
          push @array,
            ref $v eq "HASH" ? $this->Hash2Array($v, @_, $k) : [ @_, $k, $v ];
        }
      
        return @array;
      }
      

      在所有这些解决方案之间进行性能比较会很有趣...

      【讨论】:

        【解决方案4】:

        我认为制作了一个更好的 axeman 版本。至少没有 -> 和 \shift 对我来说更容易理解。 3 行没有子程序。

        带子程序

        sub to_nested_hash {
            my $h=shift;
            my($ref,$value)=(\$h,pop);
            $ref=\%{$$ref}{$_} foreach(@_);
            $$ref=$value;
            return $h;
        }
        
        my $z={};
        to_nested_hash($z,1,2,3,'testing123');
        

        无子程序

        my $z={};
        
        my $ref=\$z; #scalar reference of a variable which contains a hash reference
        $ref=\%{$$ref}{$_} foreach(1,2,3); #keys
        $$ref='testing123'; #value
        
        #with %z hash variable just do double backslash to get the scalar reference
        #my $ref=\\%z;
        

        结果:

        $VAR1 = {
                  '1' => {
                           '2' => {
                                    '3' => 'testing123'
                                  }
                         }
                };
        

        【讨论】:

        • 没有子例程的方法没有为我编译。 $ref=\%{$$ref}{$_} foreach(1,2,3); 有问题。
        猜你喜欢
        • 2020-01-30
        • 2011-02-23
        • 2015-08-12
        • 1970-01-01
        • 2014-07-23
        • 2019-03-21
        • 1970-01-01
        • 2012-11-11
        • 1970-01-01
        相关资源
        最近更新 更多