tutorial strawberry logo historia perl

strawberry - ¿Cosas comunes para Perl?



perl tutorial (29)

La pregunta sobre las características ocultas de Perl arrojó al menos una respuesta que podría considerarse una característica o una función incorrecta. Parecía lógico continuar con esta pregunta: ¿cuáles son los errores no obvios comunes en Perl? Las cosas que parecen que deberían funcionar, pero no es así.

No daré pautas sobre cómo estructurar las respuestas, o qué es "demasiado fácil" para que se lo considere una basura, ya que para eso es la votación.

Tabla de respuestas

Sintaxis

Semántica / Características del lenguaje

Depuración

Mejores prácticas

Meta-respuestas

Ver también: ASP.NET - errores comunes


"mis" declaraciones deben usar paréntesis alrededor de listas de variables

use strict; my $a = 1; mysub(); print "a is $a/n"; sub { my $b, $a; # Gotcha! $a = 2; }

Imprime a es 2 porque my declaración solo se aplicó a $b (la mención de $a en esa línea simplemente no hizo nada). Tenga en cuenta que esto sucede sin previo aviso, incluso cuando está en uso "use strict".

Agregar "usar advertencias" (o el indicador -w) mejora mucho las cosas, ya que Perl dice que falta paréntesis alrededor de "mi" lista . Esto muestra, como muchos ya lo han hecho, por qué tanto los pragmas estrictos como los de advertencia son siempre una buena idea.


Uso del valor no inicializado en la concatenación ...

Este me vuelve loco. Usted tiene una impresión que incluye una serie de variables, como:

print "$label: $field1, $field2, $field3/n";

Y una de las variables es undef . Consideras que esto es un error en tu programa, es por eso que estabas usando el pragma "estricto". Tal vez su esquema de base de datos permitió NULL en un campo que no esperaba, o se olvidó de inicializar una variable, etc. Pero todo el mensaje de error que le dice es que se encontró un valor no inicializado durante una operación de concatenación ( . ). ¡Si solo te dijera el nombre de la variable que no fue inicializada!

Debido a que Perl no desea imprimir el nombre de la variable en el mensaje de error por algún motivo, usted termina rastreando estableciendo un punto de interrupción (para ver qué variable es undef ), o agregando código para verificar la condición. Muy molesto cuando solo ocurre una vez entre miles en un script CGI y no puede volver a crearlo fácilmente.


¿Qué tal el hecho de que

@array = split( / /, $string );

no da el mismo resultado que

@array = split( '' '', $string );

si $ string tiene espacios iniciales?

Eso podría tomar a algunas personas por sorpresa.


¿Qué valores espera que contenga @_ en el siguiente escenario?

sub foo { } # empty subroutine called in parameters bar( foo(), "The second parameter." ) ;

Esperaría recibir en el bar :

undef, "The second parameter."

Pero @_ contiene solo el segundo parámetro, al menos cuando se prueba con perl 5.88.


Agregar paréntesis adicionales nunca podría cambiar el significado del código , ¿verdad? ¿Derecha?

my @x = ( "A" x 5 ); # @x contains 1 element, "AAAAA" my @y = (("A") x 5 ); # @y contains 5 elements: "A", "A", "A", "A", "A"

Oh, es cierto, este es Perl.

EDITAR: solo para una buena medida, si se llama a x en contexto escalar, entonces los paréntesis no importan después de todo:

my $z = ( "A" x 5 ); # $z contains "AAAAA" my $w = (("A") x 5 ); # $w contains "AAAAA" too

Intuitivo.


Asignar matrices a escalares no tiene sentido para mí. Por ejemplo:

$foo = ( ''a'', ''b'', ''c'' );

Asigna ''c'' a $ foo y arroja el resto de la matriz. Este es más raro:

@foo = ( ''a'', ''b'', ''c'' ); $foo = @foo;

Parece que debería hacer lo mismo que el primer ejemplo, pero en su lugar establece $foo a la longitud de @foo , por lo que $foo == 3 .


Comparando cadenas usando == y != lugar de eq y ne . Por ejemplo:

$x = "abc"; if ($x == "abc") { # do something }

En lugar de:

$x = "abc"; if ($x eq "abc") { # do something }


El DWIMmer de Perl tiene dificultades con la notación << (aquí-documento) cuando se usa print con manejadores de archivos léxicos:

# here-doc print $fh <<EOT; foo EOT # here-doc, no interpolation print $fh <<''EOT''; foo EOT # bitshift, syntax error # Bareword "EOT" not allowed while "strict subs" in use print $fh<<EOT; foo EOT # bitshift, fatal error # Argument "EOT" isn''t numeric... # Can''t locate object method "foo" via package "EOT"... print $fh<<''EOT''; foo EOT

La solución es tener cuidado de incluir espacios en blanco entre el identificador de archivo y el << o desambiguar el identificador de archivo envolviéndolo en {} llaves:

print {$fh}<<EOT; foo EOT


El hash "constructor" no es más que una lista, y la => coma grasa no es más que azúcar sintáctica. Usted puede ser mordido por esto al confundir la sintaxis arrayref con la sintaxis de la lista () :

my %var = ( ("bar", "baz"), fred => "barney", foo => (42, 95, 22) ); # result { ''bar'' => ''baz'', ''95'' => 22, ''foo'' => 42, ''fred'' => ''barney'' }; # wanted { ''foo'' => [ 42, 95, 22 ] }


El hecho de que las comillas simples se pueden usar para reemplazar :: en identificadores.

Considerar:

use strict; print "$foo"; #-- Won''t compile under use strict print "$foo''s fun!"; #-- Compiles just fine, refers to $foo::s

Conduciendo al siguiente problema:

use strict; my $name = "John"; print "$name''s name is ''$name''"; # prints: # name is ''John''

La forma recomendada de evitar esto es utilizar llaves alrededor de su nombre de variable:

print "${name}''s name is ''$name''"; # John''s name is ''John''

Y también para use warnings , ya que te informará sobre el uso de la variable indefinida $name::s


El problema más común es comenzar sus archivos con algo diferente a

use strict; use diagnostics;

pjf agrega: Tenga en cuenta que el diagnóstico tiene un impacto significativo en el rendimiento. Se ralentiza la puesta en marcha del programa, ya que necesita cargar perldiag.pod, y hasta bleadperl desde hace unas semanas, también ralentiza e hincha las expresiones regulares porque usa $ &. Se recomienda usar advertencias y ejecutar splain en los resultados.


Esta es una meta-respuesta. Perl::Critic atrapa muchas trampas desagradables, que puede instalar y ejecutar desde la línea de comandos con el comando perlcritic , o (si está feliz de enviar su código a través de Internet y no puede personalizar su opciones) a través del sitio web Perl :: Critic .

Perl::Critic también proporciona referencias al libro Best Practices de Damian Conways Perl , incluidos los números de página. Entonces, si eres demasiado flojo para leer todo el libro, Perl::Critic aún puede decirte las partes que deberías leer.


Este problema se solucionó en Perl 5.10 - si tienes la suerte de estar trabajando en un lugar que no sea alérgico a la actualización de cosas> :-(

Hablo de la variable que es validamente cero. Ya sabes, el que causa resultados inesperados en cláusulas como:

unless ($x) { ... } $x ||= do { ... };

Perl 5.10 tiene el operador // = o definido-u .

Esto es particularmente insidioso cuando el cero válido es causado por alguna condición de borde que no se consideró en las pruebas antes de que su código pasara a producción ...


Hice esto una vez:

my $object = new Some::Random::Class->new;

Me llevó años encontrar el error. La sintaxis del método indirecto es eeevil .


La mayoría de los operadores de bucle de Perl ( foreach , map , grep ) localizan automáticamente $_ pero while(<FH>) no lo hace. Esto puede llevar a una acción extraña a distancia.


La página de perltrap enumera muchas trampas para los incautos organizadas por tipo.


Las constantes pueden redefinirse. Una forma simple de redefinir accidentalmente una constante es definir una constante como referencia.

use constant FOO => { bar => 1 }; ... my $hash = FOO; ... $hash->{bar} = 2;

Ahora FOO es {bar => 2};

Si está utilizando mod_perl (al menos en 1.3), el nuevo valor de FOO persistirá hasta que se actualice el módulo.


Modificar la matriz en la que está bucle en a para (cada uno) como en:

my @array = qw/a b c d e f g h/; for ( @array ) { my $val = shift @array; print $val, "/n"; }

se confunde y no hace lo que cabría esperar


No puede localizar las variables exportadas a menos que exporte todo el typeglob.


Olvidando anteponer la ruta del directorio a los resultados de readdir antes de hacer pruebas en esos resultados. Aquí hay un ejemplo:

#!/usr/bin/env perl use strict; use warnings; opendir my $dh, ''/path/to/directory/of/interest'' or die "Can''t open ''/path/to/directory/of/interest for reading: [$!]"; my @files = readdir $dh; # Bad in many cases; see below # my @files = map { "/path/to/directory/of/interest/$_" } readdir $dh; closedir $dh or die "Can''t close /path/to/directory/of/interest: [$!]"; for my $item (@files) { print "File: $item/n" if -f $item; # Nothing happens. No files? That''s odd... } # Scratching head...let''s see... use Data::Dumper; print Dumper @files; # Whoops, there it is...

Este gotcha se menciona en la documentación de readdir , pero creo que sigue siendo un error bastante común.


Puede imprimir en un manejador de archivo léxico: bueno.

print $out "hello, world/n";

A continuación, te das cuenta de que podría ser bueno tener un hash de filehandles:

my %out; open $out{ok}, ''>'', ''ok.txt'' or die "Could not open ok.txt for output: $!"; open $out{fail}, ''>'', ''fail.txt'' or die "Could not open fail.txt for output: $!";

Hasta aquí todo bien. Ahora intente usarlos, e imprima en uno u otro según una condición:

my $where = (frobnitz() == 10) ? ''ok'' : ''fail''; print $out{$where} "it worked!/n"; # it didn''t: compile time error

Tienes que ajustar la desreferencia de hash en un par de curlies:

print {$out{$where}} "it worked!/n"; # now it did

Este es un comportamiento completamente no intuitivo. Si no lo escuchó o lo leyó en la documentación, dudo que lo pueda resolver por su cuenta.


Referencias confusas y objetos reales:

$a = [1,2,3,4]; print $a[0];

(Debe ser uno de $a->[0] (mejor), $$a[0] , @{$a}[0] o @$a[0] )


Si eres lo suficientemente tonto como para hacerlo, Perl te permitirá declarar múltiples variables con el mismo nombre:

my ($x, @x, %x);

Debido a que Perl usa sigils para identificar el contexto en lugar del tipo de variable, esto casi garantiza la confusión cuando el código posterior usa las variables, particularmente si $x es una referencia:

$x[0] $x{key} $x->[0] $x->{key} @x[0,1] @x{''foo'', ''bar''} @$x[0,1] @$x{''foo'', ''bar''} ...


Tratar un escalar como un entero:

$n = 1729; $s = 0; $i = 0; while ($n) { $s += $n % 10; $n/=10; $i ++ } print "Sum : $s/n"; print "Number of iterations : $i/n"

Suma: 19

Número de iteraciones: 327

Idealmente debería haber tenido solo cuatro iteraciones, pero un escalar no es un int, y obtuvimos un resultado inesperado.


Unario menos con "foo" crea "-foo" :

perl -le ''print -"foo" eq "-foo" ? "true" : "false"''

Esto solo funciona si el primer caracter coincide con /[_a-zA-Z]/ . Si el primer carácter es "-" , cambia el primer carácter a "+" , y si el primer carácter es "+" , cambia el primer carácter a "-" . Si el primer caracter coincide con /[^-+_a-zA-Z]/ , intenta convertir la cadena en un número y anula el resultado.

perl -le '' print -"foo"; print -"-foo"; print -"+foo"; print -"/x{e9}"; #e acute is not in the accepted range print -"5foo"; #same thing for 5 ''

El código de arriba imprime

-foo +foo -foo -0 -5

Esta característica existe principalmente para permitir que las personas digan cosas como

my %options = ( -depth => 5, -width => 2, -height => 3, );


Usando el modificador /o con un patrón de expresiones regulares almacenado en una variable.

m/$pattern/o

Especificar /o es una promesa de que el $pattern no cambiará. Perl es lo suficientemente inteligente como para reconocer si cambió o no y recompilar la expresión regular condicional, por lo que no hay una buena razón para usar /o nunca más. Alternativamente, puede usar qr// (por ejemplo, si está obsesionado con evitar el cheque).


La respuesta de Graeme Perrow fue buena, ¡pero es aún mejor!

Dada una función típica que devuelve una buena lista en el contexto de la lista, bien podría preguntar: ¿Qué devolverá en el contexto escalar? (Por "típico" me refiero al caso común en que la documentación no dice, y suponemos que no utiliza ningún wantarray divertido de wantarray . Tal vez es una función que usted mismo escribió).

sub f { return (''a'', ''b'', ''c''); } sub g { my @x = (''a'', ''b'', ''c''); return @x; } my $x = f(); # $x is now ''c'' my $y = g(); # $y is now 3

El contexto al que se llama una función se propaga para return instrucciones en esa función.

Supongo que la persona que llamó se equivocó al querer una regla empírica simple para permitir un razonamiento eficiente sobre el comportamiento del código . Tienes razón, Perl, es mejor para el personaje que llama deslizarse por el código fuente de la función llamada cada vez .


Errores de nombres de variables ... Una vez pasé todo un código de solución de problemas por la tarde que no se comportaba correctamente solo para encontrar un error tipográfico en un nombre de variable, que no es un error en Perl, sino la declaración de una nueva variable.


my $x = <>; do { next if $x !~ /TODO/s*[:-]/; ... } while ( $x );

do no es un bucle No puedes el next . Es una instrucción para realizar un bloqueo. Es lo mismo que

$inc++ while <>;

A pesar de eso, parece una construcción en la familia de idiomas C.