Página 1 de 1

Odd number of elements in hash assignment

NotaPublicado: 2017-11-26 06:58 @332
por 3mgcantarero
Buenos días.

Estoy intentando crear un módulo de perl que me recorra línea a línea un archivo Uniprot y que devuelva un hash multidimensional a partir del cual el programa principal me permita imprimir a la salida estándar los campos ID, DE, DT, OS y SQ de cada entrada del archivo uniprot.

Sé que hay alguna entrada a la que le falta alguno de los campos, y creo que esto es lo que está haciendo que cuando lanzo el programa principal obtenga el siguiente mensaje de error en la consola:

Odd number of elements in hash assignment at Intento1.pl line 28, <INFILE> line 2.

He intentado declarar las variables vacías antes de declarar el hash, pero eso tampoco me funciona... ¿Alguien me podría ayudar?

Muchas gracias.

Módulo
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. package Utils::Uniprot;
  2. use strict;
  3. use warnings;
  4.  
  5. use Exporter;
  6. our @ISA    = qw(Exporter);
  7. our @EXPORT = qw(ParseUniprot);
  8.  
  9. my $path;
  10. my $ID;
  11. my $DE;
  12. my $OS;
  13. my $DT;
  14. my $SQ;
  15.  
  16. sub ParseUniprot {
  17.     my ($path) = @_;
  18.     open( OUT, ">resultado.txt" );
  19.     my $linea;
  20.     open( INFILE, "$path" ) || die "$!:No se puede leer el archivo Uniprot_archea.txt\n\n";
  21.     while ( $linea = <INFILE> ) {
  22.         chomp $linea;
  23.         if ( $linea =~ /^ID\s{3}(\S+)\s+\S+\s+\d+/ ) {
  24.             $ID = $1;
  25.             next;
  26.         }
  27.         if ( $linea =~ /^DE\s+\w+/ ) {
  28.             $DE .= $linea;
  29.             $DE =~ s/DE//g;
  30.             next;
  31.         }
  32.         if ( $linea =~ /^DT\s{3}\S+\s\w{5}\s\w{7}\s\d+/ ) {
  33.             $DT = $linea;
  34.             $DT =~ s/DT//g;
  35.             next;
  36.         }
  37.         if ( $linea =~ /^OS\s{3}/ ) {
  38.             $OS .= $linea;
  39.             $OS =~ s/OS//;
  40.             next;
  41.         }
  42.         if ( $linea =~ /^\s{3}/ ) {
  43.             $SQ .= $linea;
  44.             $SQ =~ s/\s+//g;
  45.         }
  46.  
  47.         $DE = ();
  48.         $OS = ();
  49.         $DT = ();
  50.         $SQ = ();
  51.         my @list = ( $DE, $DT, $OS, $SQ );
  52.  
  53.         my %resultados;
  54.         $resultados{ID} = [@list];
  55.         return (%resultados) . "\n";
  56.     }
  57.     close OUT;
  58. }
  59. 1;
  60.  
Coloreado en 0.006 segundos, usando GeSHi 1.0.8.4


Programa principal
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. BEGIN{
  7.         use Getopt::Long;
  8.         die "You have and *old* version of Getopt::Long, please",
  9.             "update it asap!!\n"
  10.             unless Getopt::Long->VERSION>=2.5;
  11. }
  12.  
  13. use Getopt::Long;
  14. use lib '/home/alumno/Perl_modules/';
  15. use Utils::Uniprot;
  16.  
  17. my $path;
  18. my %info;
  19. my %resultados;
  20. my $dato;
  21.  
  22. GetOptions (
  23.                 "path=s"=>\$path,
  24.  
  25. );
  26.  
  27. if ($path) {
  28.         my %info=ParseUniprot($path);
  29.         foreach my $info (sort keys %resultados){
  30.              print $info{ID}[0]."\n";  
  31.              print $info{ID}[1]."\n";
  32.              print $info{ID}[2]."\n";
  33.              print $info{ID}[3]."\n";
  34.         }
  35. }
  36. else {
  37.         print "USAGE:: perl $0 -path=/home/alumno/asignatura_10/Semana4/Uniprot_archea.txt\n";
  38. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Re: Odd number of elements in hash assignment

NotaPublicado: 2017-11-26 08:58 @415
por explorer
Es un error frecuente. El mensaje dice "Número impar de elementos en la asignación de un hash".

En el momento de asignar valores a un hash, se espera un número par de elementos. Recordar que un hash se compone de pares clave/valor.

En la línea 28 quieres recibir unos valores y guardarlo en el hash %info. Y resulta que está recibiendo un número impar.

Si hacemos una prueba en la línea de comandos, lo vemos más claro:
Sintáxis: [ Descargar ] [ Ocultar ]
Using bash Syntax Highlighting
  1. > perl -w -MData::Dumper -e 'my %y = a(); print Dumper \%y; sub a { %x=(ID=>[("","","","")]); return (%x) . "\n" }'
  2. Odd number of elements in hash assignment at -e line 1.
  3. $VAR1 = {
  4.           '1
  5. ' => undef
  6.         };
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Aquí estoy reproduciendo el comportamiento del módulo y de tu programa, que está llamando a una subrutina a(), que define un hash %x, y luego hace el return igual a como lo tienes en tu módulo. Y pasa algo raro. Fíjate que en %y acaba recibiendo una cadena de texto "1\n", y saca el mismo mensaje de error.

El error está en el return. Tienes puesto:

return (%resultados) . "\n";

Lo que hace esa línea es reducir todo el hash a un único valor, y luego le concatena un retorno de carro. Y me parece que esto no es lo que quieres que devuelva la subrutina. Lo que quieres es que devuelva todo el hash, así que lo que hay que poner es

return %resultados;

Y ya obtenemos el resultado esperado:

Sintáxis: [ Descargar ] [ Ocultar ]
Using bash Syntax Highlighting
  1. > perl -w -MData::Dumper -e 'my %y = a(); print Dumper \%y; sub a { %x=(ID=>[("","","","")]); return %x }'
  2. $VAR1 = {
  3.           'ID' => [
  4.                     '',
  5.                     '',
  6.                     '',
  7.                     ''
  8.                   ]
  9.         };
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Otros detalles curiosos del código:
En el módulo:
  • 18: abres un archivo, pero no escribes nada en él
  • 42: si $línea comienza con tres espacios en blanco, realiza una asignación a $SQ, y luego sigue por las líneas 47 y siguientes
  • 47: pone todas las variables definidas unas líneas antes, a nada (?)
  • 51: declara y define un array @list con el contenido de las variables, pero antes han sido limpiadas, así que está creando un array de 4 elementos vacíos
  • 54: estás guardando la lista en la clave 'ID', que es fija para todas las líneas leídas. ¿No será '$ID'?
En el programa:
  • 29: el hash $resultados nunca se ha definido, por lo que ese bucle nunca se ejecuta. Es posible que la línea 28 en realidad sea $resultados = ParseUniprot($path);

Re: Odd number of elements in hash assignment

NotaPublicado: 2017-11-26 11:49 @534
por 3mgcantarero
Hola.

Es cierto que he mandado un archivo que no es el definitivo y podían quedar cosas como el archivo .txt que abría y en el que no escribía nada. Ese archivo lo voy creando para ver qué voy recogiendo la información que necesito en cada variable.

Estoy bastante atascada con el tema del hash. Creo que no termino de entender muy bien cómo funciona y menos cuando se trata de un hash múltiple en el que hay que almacenar varios valores para varias claves y no consigo hacer que funcione y menos aún que el programa principal recoja el hash generado en el módulo para imprimir por la salida estándar el contenido...

Si me pudieras echar una mano, o indicar algún manual que me pudiera aclarar un poco...

Un saludo y muchas gracias.

Mónica.

Re: Odd number of elements in hash assignment

NotaPublicado: 2017-11-26 19:46 @865
por explorer
Hay otro detalle: fíjate que el return está metido dentro del bucle de lectura de líneas. Lo normal es que esté al final.

Y para pasar los %resultados al programa principal, te vale con el return %resultados que te puse.

Cuando quieras ver el contenido de una estructura más o menos compleja, puedes usar el módulo Data::Dumper, que viene incluida con tu Perl. Por ejemplo:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4.  
  5. use Data::Dumper;
  6.  
  7. my %hash = (
  8.         clave1  => [ 1, 2, 3, 4 ],
  9.         clave2  => {
  10.                         a => 'A',
  11.                         b => 'B',
  12.         },
  13.         clave3  => [qw( hola adios )],
  14. );
  15.  
  16. print Dumper \%hash;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Este programa genera esta salida:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
$VAR1 = {
          'clave2' => {
                        'a' => 'A',
                        'b' => 'B'
                      },
          'clave3' => [
                        'hola',
                        'adios'
                      ],
          'clave1' => [
                        1,
                        2,
                        3,
                        4
                      ]
        };
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Y ya ves que te muestra una estructura compleja almacena en %hash.
(Las claves salen desordenadas porque así las guardan los hashes).

Si quieres leer documentación, tienes la última documentación en
https://github.com/perldocES/perldoc-es ... translated

o un poco más bonita en
https://metacpan.org/release/POD2-ES

https://github.com/perldocES/perldoc-es ... erllol.pod te comenta cómo hacer estructuras de arrays de arrays.

El truco para hace estructuras complejas (en tu programa metes arrays como valores de un hash) es que debes usar referencias, que al fin y al cabo son valores escalares, por lo que se pueden guardar como valores del hash. Cuando haces

$resultados{ID} = [@list];

estás "desplegando" los valores almacenados en @list, agrupados con los corchetes creas un array anónimo, cuya referencia guardas en el hash %resultados, en la clave 'ID'. Si le haces un Dumper, sale

Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
    $VAR1 = {
              'ID' => [
                        '',
                        '',
                        '',
                        ''
                      ]
            };
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

(no salen valores dentro del array, por lo que te comenté en el correo anterior).

Para aprender sobre referencias, está el documento perlref, pero se te puede hacer algo pesado.
https://github.com/perldocES/perldoc-es ... erlref.pod

Quizás más amigable sea perlreftut, que es un tutorial:
https://github.com/perldocES/perldoc-es ... reftut.pod

Toda esta documentación está en tu propio ordenador, que puedes ver con el comando perldoc perlreftut, pero en inglés. Si quieres verlo en castellano, debes instalarte el paquete POD2::ES, pero si quieres puedes verlas vía web, con los enlaces que te he pasado.

Para entender los hashes, también está perldata, pero es muy extensa. Busca por la palabra hash.
https://github.com/perldocES/perldoc-es ... rldata.pod

Lo básico es: los hashes almacenan pares clave/valor. Y las claves son únicas, por lo que solo se puede almacenar un valor para esa clave. Y los valores son siempre escalares (números, letras, cadenas de texto, referencias a otras estructuras).

Truco: hay código Perl para leer archivos Uniprot por Internet. Puedes mirarlos para inspirarte. Inténtalo. Si te atascas, vuelve por aquí y te daremos más pistas.