• Publicidad

Error: Use of uninitialized value in string

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

Error: Use of uninitialized value in string

Notapor rookie » 2006-05-26 16:51 @744

¿Cuál es el error en esta línea? Llevo un buen rato trabado con este error.
Código: Seleccionar todo
$cadena="COMO ESTAN EL DIA DE HOY";
my @basura = ("Y","DEL","DE","LA","LAS","EL");
@array=split(/ /,$cadena); $tam=scalar(@array);
for ($i=0;$i<$tam;$i++)
                {for ($j=0;$j<6;$j++)
                    { if($array[$i] eq $basura[$j])        <--  AQUI  !! <--  linea 86
                   { delete($array[$i]);  last;}
                    }}

El error marcado es:
Use of uninitialized value in string eq at rfc_curp_.pl line 86.
rookie
Perlero nuevo
Perlero nuevo
 
Mensajes: 41
Registrado: 2006-03-01 18:48 @825

Publicidad

Notapor kidd » 2006-05-26 20:08 @881

Hola:

A mi no me marca error, lo más seguro es que uno de tus arreglos tienes más elementos que el otro, entonces está intentando sacar un elemento que no existe.

Checa el tamaño de ambos, ese debe de ser el problema.


Saludos
Uriel Lizama Perl programmer fundador de Perl en Español
Perl Programming Language
Avatar de Usuario
kidd
Creador de Perl en Español
Creador de Perl en Español
 
Mensajes: 1166
Registrado: 2003-10-15 16:52 @744
Ubicación: México

Notapor explorer » 2006-05-27 04:05 @212

El problema es en el delete.

Efectivamente, si ejecutamos el código que pones, a mí tampoco me dá error. El problema es que al hacer el delete, lo que estamos haciendo es dejar como indefinidos los valores que hay dentro del array.

Me explico: si hacemos un delete de un elemento que esté al final del array, el elemento desaparece y el array se reduce en una unidad. Pero si el elemento está en mitad o al principio, lo que hace Perl es dejarlo en el estado anterior a ser inicializado. Si se hiciese un exists($array[$i]) nos devolverá falso. Pero su posición dentro del array seguirá existiendo.

Para eliminar elementos dentro de un array, hay que usar splice. Con splice(@array,$i,1); estamos quitando el elemento i-ésimo del array.

Pero ahora tenemos otro problema. Supongamos que el array tiene 6 elementos, y que el elemento a eliminar está en la tercera posición. Al hacer el splice, lo eliminamos. El array se reduce a 5. El problema es que en el bucle for, al aumentar el valor del índice $i para apuntar al siguiente elemento, resulta que estamos apuntando al siguiente elemento de donde realmente deberíamos mirar (hemos quitado un elemento y el resto del array se han desplazado). Y un segundo problema: el bucle for sigue pensando que el array sigue teniendo 6 elementos. Nos sacará mensajes de error.

Una solución ingeniosa es... leer el array desde el final:
Código: Seleccionar todo
    1 #!/usr/bin/perl -l
    2 use strict;
    3 use warnings;
    4 use diagnostics;
    5
    6 my @basura = qw(Y DEL DE LA LAS EL);
    7 my $cadena = 'COMO ESTAN EL DIA DE HOY';
    8
    9 my @array = split(/ /,$cadena);
   10 print "Original: ", join(" ", @array);
   11 for ( my $i = $#array; $i >= 0; $i--) {
   12     for my $j ( 0 .. $#basura) {
   13         if( $array[$i] eq $basura[$j] ) {
   14             splice(@array,$i,1);
   15             last;
   16         }
   17     }
   18 }
   19 print "Resultado: ", join(" ", @array);
Es tu mismo código salvo algún cambio estético y, además, en la línea 11, recorremos el array desde atrás.

Yendo hacia atrás, en el caso de que tengamos que eliminar un elemento, no habrá problemas con el índice $i (porque siempre estamos apuntando al siguiente elemento a analizar) y tampoco con el problema de la longitud del array (siempre $i se dirige hacia el mismo valor, el 0).

De todas formas, se puede aprovechar esta situación para enseñar una técnica en la que se piensa poco: un bucle no es un caballo desbocado a galope. Podemos pedirle a nuestra máquina que repiense lo que está haciendo:
Código: Seleccionar todo
    1 #!/usr/bin/perl -l
    2 use strict;
    3 use warnings;
    4
    5 my @basura = qw(Y DEL DE LA LAS EL);
    6 my $cadena = 'COMO ESTAN EL DIA DE HOY';
    7
    8 my @array = split(/ /,$cadena);
    9 print "Original: ", join(" ", @array);
   10
   11 BUCLE:
   12 for ( my $i = 0; $i < @array; $i++ ) {
   13     for ( my $j = 0; $j < @basura; $j++ ) {
   14         if( $array[$i] eq $basura[$j] ) {
   15             splice(@array,$i,1);
   16             redo BUCLE;
   17         }
   18     }
   19 }
   20 print "Resultado: ", join(" ", @array);
* En la línea 11, marco el bucle siguiente con una etiqueta.
* En la 12, es un bucle normal, pero aquí incluímos como test el actual valor del tamaño del array (número de elementos).
* Sigue todo normal hasta el momento del borrado del elemento. En la línea 16, como hemos cambiado la situación (un cambio en @array), le decimos que rehaga la comprobación del bucle BUCLE (redo BUCLE). Es decir, Perl sale del bucle for interno y entra en el for externo, con el MISMO valor de $i, pero comprobando si ese valor de $i ha superado el límite del tamaño de @array.

Nosotros sabemos que si relanzamos el bucle con el mismo valor de $i, en esa vuelta estaremos chequeando un nuevo elemento de @array, porque hemos desplazado todos los elementos una posición desde el final, al eliminar antes el elemento i-ésimo, por lo que en esa posición estará un nuevo elemento.

redo se utiliza poco, pero viene muy bien para estos casos en los que las circunstancias y los elementos de los bucles cambian, y hay que volver a comprobar los test.

Finalmente, estudia esta otra versión:
Código: Seleccionar todo
#!/usr/bin/perl -l
use strict;
use warnings;

my @basura = qw(Y DEL DE LA LAS EL);
my $cadena = 'COMO ESTAN EL DIA DE HOY';
my @verdad;
my @cadena = split(/ /,$cadena);
print "Original: ", join(" ", @cadena);

PALABRA:
foreach my $palabra ( @cadena ) {
    foreach my $basura ( @basura ) {
        next PALABRA if $palabra eq $basura; # Miramos la siguiente palabra si la actual es basura
    }
    push @verdad, $palabra;                  # Si no es basura, nos la guardamos
}
print "Resultado: ", join(" ", @verdad);


Interesante. En este hilo de discusión hemos tratado de los tres controles de bucles: last, next y redo. Faltaría hablar del bloque continue, pero ese sí que es más raro de ver...
Última edición por explorer el 2006-06-14 05:59 @291, editado 2 veces en total
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

Notapor rookie » 2006-05-30 13:35 @607

Saludos
Al parecer el problema quedó solucionado al utilizar splice() para borrar los elementos en lugar de delete() y al hacer un conteo inverso en el ciclo.
Saludos y hasta pronto.
rookie
Perlero nuevo
Perlero nuevo
 
Mensajes: 41
Registrado: 2006-03-01 18:48 @825


Volver a Básico

¿Quién está conectado?

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