在Perl中动态/递归地构建哈希?

时间:2010-12-30 00:04:56

标签: perl hash recursion perl-data-structures

我是Perl的新手,我正在尝试以递归方式构建哈希并且无处可去。我尝试搜索动态构建哈希的教程,但我能找到的只是关于哈希的介绍性文章。如果你指出我正确的方向或建议一篇好文章/教程,我将不胜感激。

我正在尝试从具有

形式的路径的文件中读取
one/two/three
four
five/six/seven/eight

我想构建像

这样的哈希
VAR = {
    one : {
        two : {
            three : ""
        }
    }
    four : ""
    five : {
        six : {
            seven : {
                 eight : ""
            }
        }
    }
}

我目前使用的脚本是:

my $finalhash = {}; 
my @input = <>;

sub constructHash {
    my ($hashrf, $line) = @_; 
    @elements = split(/\//, $line);
    if(@elements > 1) {
        $hashrf->{shift @elements} = constructHash($hashrf->{$elements[0]}, @elements ); 
    } else {
        $hashrf->{shift @elements} = ""; 
    }
    return $hashrf;
}

foreach $lines (@input) {
    $finalhash = constructHash($finalhash, $lines);
}

5 个答案:

答案 0 :(得分:7)

Data::Diver很好地涵盖了这个利基,人们不应该重新发明轮子。

use strict;
use warnings;
use Data::Diver 'DiveVal';
use Data::Dumper;

my $root = {};
while ( my $line = <DATA> ) {
    chomp($line);
    DiveVal( $root, split m!/!, $line ) = '';
}
print Dumper $root;
__DATA__
one/two/three
four
five/six/seven/eight

答案 1 :(得分:6)

这有点牵强,但它确实有效:

sub insert {
  my ($ref, $head, @tail) = @_;
  if ( @tail ) { insert( \%{$ref->{$head}}, @tail ) }
  else         {            $ref->{$head} = ''      }
}

my %hash;
chomp and insert \%hash, split( '/', $_ ) while <>;

它依赖于自动更新,对于初学者而言,这无疑是有点先进的。

你的问题的任何答案可能会有点扭曲,你要求叶子中的空字符串,它与节点的哈希值有不同的“类型”,并且需要不同的解除引用操作。

答案 2 :(得分:4)

我从来没有做过这样的事情,所以这种做法可能是错的,但好吧,这是我的镜头:

use 5.013;
use warnings;
use Data::Dumper;

sub construct {
   my $hash = shift;
   return unless @_;

   return construct($hash->{shift()} //= {}, @_);
}

my %hash;

while (<DATA>) {
   chomp;
   construct(\%hash, split m!/!);
}

say Dumper \%hash;

__DATA__
one/two/three
four
five/six/seven/eight

编辑:已修复!

EDIT2:A(我认为)尾部调用优化版本,因为!

sub construct {
   my $hash = shift;
   return unless @_;
   unshift @_, $hash->{shift()} //=  @_ ? {} : '';

   goto &construct;
}

答案 3 :(得分:3)

我运行了你的代码并发现了一些问题:

  • 您没有正确定位@elements
  • 使用该递归,您将创建一个引用自身的哈希,这不是您想要的内容。
  • 在你最外面的调用中,第二个arg到constructHash()是一个字符串,但是在内部的递归调用中,你传递了一个@elements
  • 的数组

试试这个。

use Data::Dumper;

my $finalhash = {}; 
my @input = split "\n", <<INPUT;
one/two/three
four
five/six/seven/eight
INPUT

sub constructHash {
    my $line = shift; 
    my ($first, $remainder) = split(/\//, $line,2);

    if ($remainder) {
        return { $first => constructHash($remainder) } ; 
    } else {
        return { $first , "" }; 
    }
}

foreach $lines (@input) {
    my $linehash = constructHash($lines);
    my $firstkey = (keys %$linehash)[0];
#    print Dumper $linehash;
    $finalhash->{$firstkey} = $linehash->{$firstkey};
} 


print Dumper $finalhash;

它产生

$VAR1 = {
          'five' => {
                      'six' => {
                                 'seven' => {
                                              'eight' => ''
                                            }
                               }
                    },
          'one' => {
                     'two' => {
                                'three' => ''
                              }
                   },
          'four' => ''
        };

请记住,Perl哈希值不是有序的。

答案 4 :(得分:1)