Dynamically/recursively building hashes in Perl?

前端 未结 5 1128
北恋
北恋 2020-12-19 04:45

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 wer

相关标签:
5条回答
  • 2020-12-19 05:02

    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
    
    0 讨论(0)
  • 2020-12-19 05:03

    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;
    }
    
    0 讨论(0)
  • 2020-12-19 05:05

    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.

    0 讨论(0)
  • 2020-12-19 05:17

    The basics:

    • the Perl data structure cookbook
    • autovivification
    0 讨论(0)
  • 2020-12-19 05:20

    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.

    0 讨论(0)
提交回复
热议问题