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

发布于 2024-10-09 12:33:14 字数 917 浏览 11 评论 0原文

我对 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);
}

I'm quite new to Perl and I'm trying to build a hash recursively and getting nowhere. I tried searching for tutorials to dynamically build hashes, but all I could find were introductory articles about hashes. I would be grateful if you point me towards the right direction or suggest a nice article/tutorial.

I'm trying to read from a file which has paths in the form of

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

and I want to build a hash like

VAR = {
    one : {
        two : {
            three : ""
        }
    }
    four : ""
    five : {
        six : {
            seven : {
                 eight : ""
            }
        }
    }
}

The script I'm using currently is :

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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

最偏执的依靠 2024-10-16 12:33:15

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

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:(我认为)尾调用优化版本,因为!

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

   goto &construct;
}

I've never done something like this, so this approach is likely to be wrong, but well, here's my shot:

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

EDIT: Fixed!

EDIT2: A (I think) tail-call optimized version, because!

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

   goto &construct;
}
你如我软肋 2024-10-16 12:33:15

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

  • 您没有正确确定 @elements 的范围。
  • 通过该递归,您将创建一个引用自身的哈希,这不是您想要的。
  • 在最外层的调用中,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 哈希值是没有排序的。

I ran your code and found a few problems:

  • you haven't scoped @elements properly.
  • with that recursion you're creating a hash that references itself, which is not what you want.
  • in your outermost call, the second arg to constructHash() is a string, but on the recursive call inside, you pass an array of @elements

Try this.

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;

It produces

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

Remember, Perl hashes aren't ordered.

早茶月光 2024-10-16 12:33:14

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

Data::Diver covers this niche so well that people shouldn't reinvent the wheel.

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
月下客 2024-10-16 12:33:14

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

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

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

它依赖于自动生存,这对于初学者来说确实有点高级。

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

This is a bit far-fetched, but it works:

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

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

It relies on autovivification, which is admittedly a bit advanced for a beginner.

What would probably make any answer to your question a bit twisted is that you ask for empty strings in the leaves, which is of a different "type" than the hashes of the nodes, and requires a different dereferencing operation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文