• Publicidad

Crear variables mediante for

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Crear variables mediante for

Notapor hjkmd » 2012-06-03 11:26 @518

Lo primero es dar las gracias a la gente que hace posible este foro, ya que estoy empezando a programar y aquí hay mucho donde aprender.

Ahora mi problema: estoy intentando crear una lista de hashes mediante un bucle, pero no sé si se puede hacer y lógicamente mi código no funciona.

La pregunta en definitiva sería: ¿se pueden crear variables usando otras variables como parte del nombre?

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5. use diagnostics;
  6.  
  7. for (my $i=0; $i < 256; $i++) {
  8.         my $HEX = sprintf ("%X", $i);
  9.         print "$HEX\n";
  10.         my %hash_$HEX; <== Aquí está el fallo
  11. }
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Estoy intentando crear desde %hash_0 hasta %hash_FF y crearlos todos de uno en uno me sería prácticamente imposible, ya que luego pretendo realizar más operaciones con cada uno.

Un saludo y muchas gracias por adelantado. :wink:
hjkmd
Perlero nuevo
Perlero nuevo
 
Mensajes: 3
Registrado: 2012-05-31 21:04 @920

Publicidad

Re: Crear variables mediante for

Notapor explorer » 2012-06-03 12:08 @547

Bienvenido a los foros de Perl en Español, hjkmd.

Sí que se puede, pero el propio Perl, con el 'use strict', te avisa de que no lo hagas...

Y no tienes por qué hacerlo por que... no es necesario hacerlo...

A ver... Tú lo que quieres es tener una colección de 256 hash, ¿no?. Pues en Perl, a una colección la llamamos array.

Entonces, solo tenemos que crear y manejar una estructura bidimensional: la primera coordenada es el índice dentro del array, y el valor correspondiente a ese índice (la segunda coordenada) es una referencia al hash que almacenará el resto de la información.

Ejemplo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.14;
  3.  
  4. my @almacen;                                    # aquí guardaremos todo
  5.  
  6. ## Caso 1. Ejemplo de inicialización
  7. # Supongamos que queremos tener un hash { id => $i } para cada $i posible
  8.  
  9. for my $i (0 .. 255) {
  10.     $almacen[ $i ] -> { 'id' } = $i;            # con espacios, para más claridad
  11. }
  12.  
  13. ## Caso 2. Acceso
  14. # Supongamos que queremos calcular la suma de todos los 'id'
  15. my $total = 0;
  16.  
  17. for my $i (0 .. 255) {
  18.  
  19.     $total += $almacen[$i]{'id'};               # sin espacios ni '->', para abreviar
  20. }
  21.  
  22. say $total;
  23.  
  24. ## Caso 3. Entrada de datos aleatoria
  25. # Leemos datos desde el teclado
  26. say "Introduzca la clave (de 0 a 255) y el valor (separados por un espacio):";
  27. my $entrada = readline;
  28. chomp $entrada;
  29. my($clave, $valor) = split " ", $entrada;
  30.  
  31. $almacen[$clave]{'valor'} = $valor;             # crea/actualiza entrada 'valor'
  32.                                                 # en el hash referenciado por $clave
  33.  
  34. # comprobación
  35. say "almacen[$clave]->{'valor'} = $almacen[$clave]{'valor'}";
  36.  
  37. __END__
  38. 32640
  39. Introduzca la clave (de 0 a 255) y el valor (separados por un espacio):
  40. 3 23
  41. almacen[3]->{'valor'} = 23
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Más información en tu propio ordenador en perldoc perldsc, y en la Web (traducido).
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: Crear variables mediante for

Notapor hjkmd » 2012-06-03 13:56 @622

Muchísimas gracias por la respuesta, explorer, pero llevo cuatro días con Perl y me cuesta un poco entenderlo. Mejor te explico lo que quiero hacer y te dejo el código que tengo:

Tengo dos archivos bastante grandes con campos separados por tabulaciones, uno con tres campos ($A1, $A2 y $A3) y el otro con dos ($B1 y $B2).

Quiero comparar $A2 con $B1, y si coinciden, imprimir en otro archivo $A1, $B2 y $A3.
$A2 y $B1 son huellas de archivos MD5, por lo tanto tienen formato hexadecimal.

Esto es lo que yo he hecho para compararlos de una manera "eficiente" (crean 16 hashes por cada archivo usando la inicial):

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use strict;
  2. use warnings;
  3. use Time::HiRes qw(time);
  4.  
  5. my $inicio  = time();
  6. my $entrada = Archivo1;
  7. chomp($entrada);
  8. open( ENTRADA, "$entrada" ) or die "Error: No se puede abrir $entrada!";
  9. my @entrada = <ENTRADA>;
  10. close(ENTRADA);
  11. my $entrada2 = Archivo2;
  12. chomp($entrada2);
  13. open( ENTRADA, "$entrada2" ) or die "Error: No se puede abrir $entrada2!";
  14. my @entrada2 = <ENTRADA2>;
  15. close(ENTRADA2);
  16. my $salida = Resultado;
  17. chomp($salida);
  18. open( SALIDA, ">>$salida" ) or die "Error: No se puede abrir $salida!";
  19. my @salida;
  20.  
  21. # Definir 16 hashes para cada archivo de entrada (0 - F)
  22. my %A_0;
  23. my %A_1;                               # Así hasta my %A_F;
  24. my %B_0;
  25. my %B_1;                               # Así hasta my %B_F;
  26.  
  27. # Dividir las líneas y llenar los hashes dependiendo de la letra inicial (@entrada1)
  28. for ( my $i = 0; $i < 16; $i++ ) {
  29.     foreach my $linea (@entrada) {
  30.         chomp($linea);
  31.         ( my $A1, my $A2, my $A3 ) = split( /\t/, $linea );
  32.         my $HEX = sprintf( "%x", $i );
  33.         my $primer = substr( $A2, 0, 1 );
  34.         if ( $primer eq "0" ) { $A_0{$A2} = "$A1        $A3"; }
  35.         if ( $primer eq "1" ) { $A_1{$A2} = "$A1        $A3"; }
  36.  
  37.         # Corto para no ocupar tanto espacio...
  38.         if ( $primer eq "f" ) { $A_F{$A2} = "$A1        $A3"; }
  39.     }
  40. }
  41.  
  42. # Dividir las líneas y llenar los hashes dependiendo de la letra inicial (@entrada2)
  43. for ( my $i = 0; $i < 16; $i++ ) {
  44.     foreach my $linea (@entrada2) {
  45.         chomp($linea);
  46.         ( my $B1, my $B2 ) = split( /\t/, $linea );
  47.         my $HEX = sprintf( "%x", $i );
  48.         my $primer = substr( $B1, 0, 1 );
  49.         if ( $primer eq "0" ) { $B_0{$B1} = $B2; }
  50.         if ( $primer eq "1" ) { $B_1{$B1} = $B2; }
  51.  
  52.         # Corto para no ocupar tanto espacio...
  53.         if ( $primer eq "f" ) { $B_F{$B1} = $B2; }
  54.     }
  55. }
  56.  
  57. # Comparar %A_0 con %B_0
  58. foreach my $clave ( keys %A_0 ) {
  59.     ( my $A1, my $A3 ) = split( /\t/, $A_0{$clave} );
  60.     foreach my $clave2 ( keys %B_0 ) {
  61.         my $B2 = "$B_0{$clave2}";
  62.         if ( $clave eq $clave2 ) {
  63.             push( @salida, "$A1 $B2     $A3\n" );
  64.             delete $A_0{$clave};
  65.             delete $B_0{$clave2};
  66.         }
  67.     }
  68. }
  69.  
  70. # Y seguir comparando el resto de hashes (%A_1 con %B_1, %A_2 con %B_2)...
  71.  
  72. # Por ultimo imprimir los resultados
  73. open( SALIDA, ">>$salida" ) or die "Error: No se puede escribir en $salida!";
  74. foreach my $linea (@salida) {
  75.     chomp($linea);
  76.     print SALIDA "$linea\n";
  77. }
  78. close(SALIDA);
  79.  
  80. my $fin = time();
  81. print "\n\n   Finalizado en ", ( $fin - $inicio ), " segundos.\n";
  82.  
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Con este código, digamos que copio, pego y modifico los nombres 16 veces (no me importa). Pero si en lugar de usar una sola letra, quiero hacerlo con las 2 primeras tengo que hacerlo 256 veces :cry:, por eso decía que si se podía hacer con for().

Muchas gracias, explorer, sobre todo por los comentarios del código.

PD:
- "say" es como "print", ¿no?
- Si se hace un sort() de los hashes ¿sería más rápido al compararlos (a pesar de lo que tarde en ordenarlo)?
Última edición por explorer el 2012-06-03 15:54 @704, editado 1 vez en total
Razón: Formateado de código con Perltidy
hjkmd
Perlero nuevo
Perlero nuevo
 
Mensajes: 3
Registrado: 2012-05-31 21:04 @920

Re: Crear variables mediante for

Notapor explorer » 2012-06-03 17:18 @762

No, no... así no hay que hacer las cosas... Debería existir una muy buena razón para tener que dividir el trabajo en un montón de variables individuales.

Ese tipo de programación es muy peligroso, porque es muy propenso a cometer errores. Puedes bailar un número y estás refiriéndote a otra variable distinta.

La ventaja de los hash es que sirven para recordar claves. No necesitas comparar uno o dos caracteres, cuando realmente lo que quieres es comparar toda la cadena.

Y la comparación es muy sencilla: si metemos todos los MD5 como claves, a la hora de buscar coincidencias, solo tenemos que ver si el campo B1 existe o no como clave. Si está, es que existía una clave MD5 del primer archivo.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use Time::HiRes qw(time);
  5.  
  6. my $inicio   = time;
  7.  
  8. my $archivo1 = 'archivo1';
  9. open(ENTRADA, $archivo1) or die "Error: No se puede abrir $archivo1:$!\n";
  10. my @archivo1 = <ENTRADA>;
  11. close(ENTRADA);
  12.  
  13. my $archivo2 = 'archivo2';
  14. open(ENTRADA2, $archivo2) or die "Error: No se puede abrir $archivo2:$!\n";
  15. my @archivo2 = <ENTRADA2>;
  16. close(ENTRADA2);
  17.  
  18. my $salida   = 'Resultado';
  19. open(SALIDA, ">>$salida") or die "Error: No se puede escribir $salida:$!\n";
  20. my @salida;
  21.  
  22. # Lugar donde guardaremos la información del archivo1
  23. my %archivo1;
  24.  
  25. # Dividir las líneas y llenar los hashes dependiendo de la letra inicial de archivo1
  26. for (@archivo1) {
  27.     chomp;
  28.     my($A1, $A2, $A3) = split;
  29.  
  30.     $archivo1{$A2} = [ $A1, $A3 ];      # Guardamos un array anónimo como valor para la clave $A2
  31. }
  32.  
  33. # Dividir las líneas y buscar las claves del archivo2 dentro del archivo1
  34. for (@archivo2) {
  35.     chomp;
  36.     my($B1, $B2) = split;
  37.  
  38.     if ($archivo1{$B1}) {                               # ¿Existe en %archivo1 alguna clave $B1?
  39.         my($A1, $A3) = @{$archivo1{$B1}};               # Recuperamos los valores
  40.  
  41.         print SALIDA join("\t", $A1, $B2, $A3), "\n";
  42.     }
  43. }
  44.  
  45. close(SALIDA);
  46.  
  47. my $fin = time;
  48. print "\n\n   Finalizado en ", ( $fin - $inicio ), " segundos.\n";
  49.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Fíjate en las diferencias:
  • los nombres de los archivos están entrecomillados simples, ya que son literales
  • no es necesario hacer chomp() de esos literales (sabemos que no tienen avances de línea)
  • usamos un hash para guardar la información del primer archivo. Usamos el MD5 como clave, y asociado a ella, como valor, guardamos un array con dos valores, que son los que luego queremos grabar en la salida
  • solo queda recorrer los datos del segundo archivo, ver si el MD5 existe como clave en el primer hash (significando que ese MD5 es igual a alguno del primer archivo. En ese caso, sacamos los valores a imprimir, imprimimos, y repetimos para todas las demás.

Sí, say() es lo mismo que print() más un "\n" al final (solo a partir de Perl v5.10)

No, no hace falta ordenar los hash, ya que con solo preguntar por clave, ya sabemos si son iguales o no (la mera existencia de una clave ya es suficiente para saber que es igual a la que buscamos).

Lo que no puedes hacer es declararlas con un for(): cuando pones un my() dentro del for(), las estás declarando dentro del for(), así que fuera de él, ya no existen.
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: Crear variables mediante for

Notapor hjkmd » 2012-06-03 18:03 @794

Joder, muchísimas gracias. Te debo unas cervezas (aunque me pilla un poco lejos Valladolid). Antes había intentado hacer algo parecido pero seguramente he debido programarlo mal porque tardaba 10 veces más que el tuyo :oops:

Me he pasado varios días intentando de todo y al final opté por la opción que mejor tiempo me daba (la que he publicado) y tu en cinco minutos lo has hecho perfecto y en muchísimo menos tiempo.

Veo que me queda un largo camino para empezar a enterarme de cómo funciona Perl realmente.

Gracias, de veras.
hjkmd
Perlero nuevo
Perlero nuevo
 
Mensajes: 3
Registrado: 2012-05-31 21:04 @920

Re: Crear variables mediante for

Notapor explorer » 2012-06-04 10:10 @465

Lo más importante es que entiendas el funcionamiento de los hash (su capacidad de ser como una "memoria"), ya que son muy útiles en todo tipo de problemas en los que tenemos que "buscar", "identificar" o "comparar" valores ya vistos antes.

Lo más oscuro de Perl(§), sería la forma de entender las referencias, los array anónimos... pero es una notación y forma de trabajar muy parecida a la de C, Pascal, y otros lenguajes. Una vez entendido el porqué son necesarios, solo queda construir la estructura como si fuera un Lego®. Y en caso de duda, usar Data::Dumper para ver el contenido de esa estructura (hay muchos ejemplos por estos foros).



(§): no te lo creas: Perl tiene cosas muchísimo más oscuras, pero afortunadamente, no necesarias para la inmensa mayor parte de los programadores :)
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 Básico

¿Quién está conectado?

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