2010-12-30 18 views
8

Soy bastante nuevo para Perl y estoy tratando de construir hash recursivamente y llegar a ninguna parte. Traté de buscar tutoriales para construir hashes dinámicamente, pero todo lo que pude encontrar fueron artículos introductorios sobre hashes. Le agradecería me señalara en la dirección correcta o sugiriera un buen artículo/tutorial.hash de construcción dinámica/recursiva en Perl?

Estoy intentando leer desde un archivo que tiene caminos en forma de

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

y quiero construir un hash como

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

El script que estoy usando actualmente es :

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

Su línea 'my ($ hashrf, $ line) = $ _;' probablemente debería leer 'my ($ hashrf, $ line) = @_;' en su lugar. –

+0

Reparado :) Pero todavía no se suma a nada. Si imprimo el valor de 'hashrf' en el ciclo, siempre es' '' ' –

Respuesta

6

Esto es un poco exagerado, pero funciona:

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

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

Se basa en autovivification, que es sin duda un poco avanzado para un principiante.

Lo que probablemente haría que una respuesta a su pregunta sea un poco retorcida es que usted pide cadenas vacías en las hojas, que es de un "tipo" diferente a los hashes de los nodos, y requiere una operación de eliminación de referencias diferente.

+3

simplemente haga que el bucle 'while' y' chomp' explícitos en su propia línea, y será muy legible _para principiante_. –

+0

Ojalá pudiera eliminar por completo el 'chomp'. Es necesario no tener nuevas líneas feas en las teclas de hoja, pero realmente no agrega nada al problema. En cuanto al 'while', las opiniones varían, pero puedes editarme si realmente piensas así. –

+0

++ porque su solución no deja hashrefs vacíos como los míos:) – Hugmeir

3

Nunca he hecho algo como esto, por lo que este enfoque es probable que sea incorrecto, pero bueno, aquí está mi tiro:

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: Reparado!

EDIT2: Una (creo) la versión optimizada de la cola, porque!

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

    goto &construct; 
} 
+0

¡Ah, eso funciona! : D, sin embargo, tiene un pequeño problema, supongamos que tengo 'one/two/three' y' one/two/four' y sobrescribirá 'one/two/three' con' one/two/four' en lugar de tener ambos. Espero que esto tenga sentido. –

+0

¡Ya está, arreglado! Hace uso del operador definido u operador, que funciona solo en un 5.10+ Perl, en Perls anteriores, tendrías que hacer algo como esto: my $ temp = $ hash -> {shift()}; constructo ((definido ($ temp)? $ temp: {}), @_) http://perldoc.perl.org/perlop.html#C-style-Logical-Defined-Or – Hugmeir

+1

Si tiene que tratar con duplicar claves ('one' para' one/two/three' y también para 'one/two/four') no puedes usar hashes como lo estás intentando. Tendrá que usar una matriz o una clave diferente, como la línea completa en sí – Nathan

3

que corrió su código y encontraron algunos problemas:

  • usted no ha de ámbito @elements correctamente.
  • con esa recursión está creando un hash que hace referencia a sí mismo, que es no lo que quiere.
  • en su llamada externa, el segundo arg a constructHash() es una cadena, pero en la llamada recursiva en el interior, se pasa una matriz de @elements

Prueba esto.

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; 

Produce

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

Recuerde, los hash de Perl no están ordenados.

+0

Gracias por las correcciones: 1) Sí, principiante error, supongo. 2) Después de publicar la pregunta, intenté 'join ('/', @elements)' para la llamada recursiva, pero no pareció funcionar. 3) Supongo que este fue el problema principal. Todavía no entiendo por qué no funcionó. ¿Puede indicarme un artículo/tutorial sobre referencias hash que lo aclararía? De nuevo muchas gracias. :) –

+0

Para 3, el problema no es realmente las referencias hash, sino el paso de parámetros. Actualmente, su sub es una función de dos argumentos (ref y línea), y esa es la forma en que lo invoca desde el nivel superior. Pero la llamada recursiva lo invoca como una función de muchos argumentos: el ref y múltiples elementos. En efecto, perdería todos los elementos, pero el primero cada vez que recursó. –

+2

@Guarav, aprendí esto mirando a ['perldoc perlref'] (http://perldoc.perl.org/perlref.html) y sus familiares (perldsc, lol, toot) y [Programación efectiva de Perl] (http : //en.wikipedia.org/wiki/Effective_Perl_Programming) también fue instrumental. – Nathan

1
+0

Ninguno de esos enlaces trata de generar recursiva/dinámicamente una estructura de datos (¿hay un mejor nombre para esto?). Aún así, tengo que amar a Perldsc. – Hugmeir

+2

Simplemente no tenía ganas de vincular a la wikipedia para la recursividad. perldsc explica de qué hashes de hash están hechos (y en Perl, no es trivial). Esa es la parte difícil de eso. No recursividad, no es el aspecto dinámico. En mi opinión, de todos modos :-) –

+0

Lo suficientemente bueno para mí:) Además, lo olvidé, ¡gracias por el enlace de perlglossary! No sabía que existía. – Hugmeir

7

Data::Diver cubre este nicho tan bien que no hay que reinventar la rueda.

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

Gracias por sugerir este módulo. Traté de buscar CPAN pero no pude encontrar esto (tal vez incluir 'hash' en la búsqueda no fue una buena idea de mi parte). Debo decir, sin embargo, aprendí mucho de reinventar la rueda (en realidad, viendo la rueda inventada por los expertos) :) –

Cuestiones relacionadas