8 votes

Construire dynamiquement/récursivement des hachages en Perl ?

Je suis assez novice en Perl et j'essaie de construire un hash récursivement sans succès. J'ai essayé de chercher des tutoriels pour construire dynamiquement des hashs, mais tout ce que j'ai pu trouver, ce sont des articles d'introduction sur les hashs. Je vous serais reconnaissant de m'indiquer la bonne direction ou de me suggérer un article/tutoriel intéressant.

J'essaie de lire un fichier dont les chemins d'accès se présentent sous la forme de

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

et je veux construire un hash comme

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

Le script que j'utilise actuellement est :

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

7voto

ysth Points 54757

Data::Diver couvre si bien ce créneau qu'il ne faut pas réinventer la roue.

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

6voto

JB. Points 12482

C'est un peu tiré par les cheveux, mais cela fonctionne :

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

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

Il repose sur l'autovivification, ce qui est, il est vrai, un peu avancé pour un débutant.

Ce qui rendrait probablement toute réponse à votre question un peu tordue, c'est que vous demandez des chaînes vides dans les feuilles, ce qui est d'un "type" différent des hachages des nœuds, et nécessite une opération de déréférencement différente.

4voto

Hugmeir Points 1239

Je n'ai jamais fait quelque chose comme ça, donc cette approche est probablement erronée, mais bon, je tente ma chance :

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 : Corrigé !

EDIT2 : Une version optimisée (je pense) pour l'appel de queue, parce que !

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

   goto &construct;
}

3voto

Nathan Points 1975

J'ai exécuté votre code et j'ai trouvé quelques problèmes :

  • vous n'avez pas fait de scoping @elements correctement.
  • avec cette récursion, vous créez un hachage qui se réfère à lui-même, ce qui est no ce que vous voulez.
  • dans votre appel externe, le deuxième arg à constructHash() est une chaîne de caractères, mais lors de l'appel récursif à l'intérieur, vous passez un tableau de @elements

Essayez ceci.

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;

Il produit

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

N'oubliez pas que les hachages Perl ne sont pas ordonnés.

1voto

JB. Points 12482

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X