• Publicidad

Leer recursivamente un HASH

Así que programas sin strict y las expresiones regulares son otro modo de hablar. Aquí encontrarás respuestas de nivel avanzado, no recomendable para los débiles de corazón.

Leer recursivamente un HASH

Notapor Javier » 2017-06-27 04:12 @217

Buenos días:

Tengo un HASH con esta estructura:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my %EW =
  2. (
  3. 'a' =>
  4.                 {
  5.                 'b'=> {},
  6.                 'c' => {},
  7.                 'd' => {},
  8.                 'e' => {},
  9.                 'f' =>
  10.                         {
  11.                         'g' => {},
  12.                         'h' => {},
  13.                         'i' => {}
  14.                         },
  15.                 'j' => {}
  16.                 }
  17. );
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Quiero pasar cada llave a otro hash, poniendo cada llave asociada a la ruta que lleva hasta ella:
a = a
b = a/b
...
g = a/f/g

Puedo hacerlo con una solución poco elegante (anidando bucles que leen las llaves):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. foreach my $n1 (keys %EW)
  2. {
  3.         $ET{$n1} = "$n1/";
  4.        
  5.         foreach my $n2 (keys $EW{$n1})
  6.         {
  7.                 $ET{$n2} = "$n1/$n2/";
  8.                
  9.                 foreach my $n3 (keys $EW{$n1}{$n2})
  10.                 {
  11.                         $ET{$n3} = "$n1/$n2/$n3/";     
  12.                 }
  13.         }
  14.  
  15.  }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿Habría alguna forma de recorrerlo hasta la profundidad que fuese? Con los bucles foreach doy por hecho que hay hasta 3 niveles de anidamiento pero no siempre será así.

He pensado en hacerlo con referencias y con una función pero no sé si voy bien encaminado.

Un saludo


Gracias
Un saludo
Javier
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2014-08-31 03:24 @183

Publicidad

Re: Leer recursivamente un HASH

Notapor explorer » 2017-06-27 10:28 @478

Pues yo creo que vas bien encaminado. Bueno, yo lo acabo de resolver así ;)

Usas las referencias para pasar de un nivel a otro de la recursión, teniendo en claro qué pasas y que no. Y luego la subrutina, que se llamará de forma recursiva.

De esta manera, puedes procesar cualquier tipo de profundidad.

Si te atascas, me avisas, y publico mi solución.

¡Adelante!
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Leer recursivamente un HASH

Notapor Javier » 2017-06-27 10:52 @495

Hola, explorer:

Creo que me pierdo. Le paso la referencia de todo el HASH a una función, para que lo saque a otro pero me estoy dejando algo:

La función a la que le paso la referencia R(\%EW) es
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. sub R
  2. {      
  3.         my $a = shift;
  4.         foreach my $b (keys $a)
  5.         {
  6.                 if (ref $a->{$b} eq 'HASH')
  7.                 {
  8.                         $et{$b} = $b;
  9.                         $a = $a->{$b};
  10.                         R($a);
  11.                 }
  12.         }
  13.  
  14. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿Dónde acumulo las rutas?
Javier
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2014-08-31 03:24 @183

Re: Leer recursivamente un HASH

Notapor explorer » 2017-06-27 21:12 @925

Creo que el problema está en la línea

keys $a

que debería ser

keys %$a

(Si $a contiene una referencia a un hash, %$a es el hash, con lo que ya podemos pedirle sus claves con keys. Bueno, según la versión que tengas de Perl, podría hacerlo de forma automática).

Pero, quizás, el mayor problema es la línea 9: estás modificando el valor de $a. Cuando se regrese de la llamada de la línea 10, la siguiente vuelta del bucle foreach lo estará haciendo sobre un $a modificado, y el if de la línea 6 fallará antes de tiempo.

Quita la línea 9 y cambia la línea 10 por

R($a->{$b});

La ruta yo la almaceno en un array, que también paso por parámetro. Pero, atención, creo un nuevo array antes de cada llamada recursiva, para evitar que las distintas llamadas interfieran sobre el mismo array, y creen rutas cada vez más grandes.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Leer recursivamente un HASH

Notapor Javier » 2017-06-30 04:12 @216

Hola:

Yo también lo saco a array, pero sólo consigo que me lo guarde por niveles y además me crea algunos vacíos:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. sub R
  2. {      
  3.         my $a = shift;
  4.         my @ab = shift;
  5.         my @ac;
  6.         push @ab , @ac;
  7.         foreach my $b (keys %$a)
  8.         {
  9.                
  10.                 if (ref $a->{$b} eq 'HASH')
  11.                 {
  12.                         push @ac , $b;
  13.                         R($a->{$b} , @ac);
  14.                 }
  15.         }
  16. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿¿Cómo podría hacer que cada llave acumule su ruta?
Javier
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2014-08-31 03:24 @183

Re: Leer recursivamente un HASH

Notapor explorer » 2017-06-30 17:50 @784

Hay un error.

En la línea 13 estás llamando a R, con dos argumentos. El primero es una referencia (lo has comprobado en la línea 10). Y el segundo, realmente, son TODOS los elementos del array @ac. Es decir, Perl "despliega" el contenido del array, y le pasa a R() un argumento por cada elemento del array.

Y... ahora viene el problema. En la línea 4 pones que el siguiente argumento a la referencia, es un solo elemento (lo sacas con un shift) y lo metes en el nuevo array @ab.

O sea... que no lees el resto de argumentos, el resto de elementos del array de la llamada.

La solución es pasar el array como referencia:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.         my @ab = @_;    # este es el array que almacena los argumentos que quedan después del shift anterior
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.                         R($a->{$b}, \@ac);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Aquí te pongo mi solución:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.20;                      # para usar las nuevas características de desreferencia. Ver perlref
  3. use experimental 'signatures';  # usar la característica experimental de signatures en las sub
  4.  
  5. my %EW = (
  6.     'a' => {
  7.         'b' => {},
  8.         'c' => {},
  9.         'd' => {},
  10.         'e' => {},
  11.         'f' => {
  12.             'g' => {},
  13.             'h' => {},
  14.             'i' => {}
  15.         },
  16.         'j' => {},
  17.     },
  18. );
  19.  
  20. my %ET;
  21. my @ruta;
  22.  
  23. procesa_hash(\%EW, []);
  24.  
  25. sub procesa_hash($hash_ref, $ruta_ref) {
  26.  
  27.     while (my($clave, $valor) = each $hash_ref->%*) {
  28.  
  29.         next if ref $valor ne 'HASH';
  30.  
  31.         my @nueva_ruta = ( $ruta_ref->@*, $clave);      # creamos una nueva ruta a esa clave
  32.  
  33.         $ET{ $clave } = join "/", @nueva_ruta;          # la guardamos
  34.  
  35.         procesa_hash($valor, \@nueva_ruta);             # procesamos el nuevo hash
  36.     }
  37. }
  38.  
  39. use Data::Dumper;
  40. say Dumper \%ET;
  41.  
  42. __END__
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
La salida es:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
$VAR1 = {
          'b' => 'a/b',
          'a' => 'a',
          'd' => 'a/d',
          'h' => 'a/f/h',
          'i' => 'a/f/i',
          'c' => 'a/c',
          'f' => 'a/f',
          'j' => 'a/j',
          'e' => 'a/e',
          'g' => 'a/f/g'
        };
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Avanzado

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 4 invitados