Gotchas communs pour Perl? [fermé]
la question sur caractéristiques cachées de Perl a donné au moins une réponse qui pourrait être considérée comme une caractéristique ou une caractéristique erronée. Il a semblé logique de donner suite à cette question: Quelles sont les erreurs non évidentes courantes en Perl? Des choses qui semblent devoir marcher, mais qui ne le font pas.
Je ne vais pas donner de directives sur la façon de structurer les réponses, ou ce qui est "trop facile" pour être considéré comme un gotcha, puisque c'est ce que le vote est pour.
Table des réponses
syntaxe
- General
- Filehandles
Sémantique / Caractéristiques Linguistiques
- General
- contexte
- Variables
débogage
"Meilleures Pratiques
- oubli de
use strict
etuse warnings
(ouuse diagnostics
) - noms variables Mal orthographiés (i.e.,
use strict
, again)
Méta-Réponses
Voir aussi: ASP.NET - gotchas commun
29 réponses
Le fait que les guillemets simples peuvent être utilisés pour remplacer :: dans les identificateurs.
prendre en considération:
use strict;
print "$foo"; #-- Won't compile under use strict
print "$foo's fun!"; #-- Compiles just fine, refers to $foo::s
conduisant au problème suivant:
use strict;
my $name = "John";
print "$name's name is '$name'";
# prints:
# name is 'John'
le moyen recommandé pour éviter cela est d'utiliser des bretelles autour de votre nom de variable:
print "${name}'s name is '$name'";
# John's name is 'John'
et aussi à use warnings
, car il vous parlera de l'utilisation de la variable non définie $name::s
vous pouvez imprimer sur une manchette lexicale: bon.
print $out "hello, world\n";
vous réalisez alors qu'il pourrait être agréable d'avoir 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: $!";
jusqu'ici, tout va bien. Maintenant, essayez de les utiliser, et imprimez à l'un ou l'autre selon une condition:
my $where = (frobnitz() == 10) ? 'ok' : 'fail';
print $out{$where} "it worked!\n"; # it didn't: compile time error
vous devez envelopper la déréférence de hachage dans une paire de boucles:
print {$out{$where}} "it worked!\n"; # now it did
c'est un comportement totalement non intuitif. Si vous Je n'ai pas entendu parler de ça, ni lu la documentation.je doute que tu puisses le découvrir par toi-même.
c'est une méta-réponse. Beaucoup de mauvais gotchas sont attrapés par Perl::Critic , que vous pouvez installer et exécuter à partir de la ligne de commande avec la commande perlcritic
, ou (si vous êtes heureux d'envoyer votre code sur Internet, et ne pas être en mesure de personnaliser vos options) via le Perl::Critic website .
Perl::Critic
fournit également des références à Damian Conways Perl Best Pratiques Livre, y compris les numéros de page. Donc, si vous êtes trop paresseux pour lire le livre entier, Perl::Critic
peut encore vous dire les bits que vous devrait lire.
Perl DWIMmer luttes avec <<
(ici-document) la notation lors de l'utilisation de print
avec lexicale descripteurs:
# 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 solution est soit de faire attention à inclure des espaces blancs entre le filehandle et le <<
ou de désambiguer le filehandle en l'enveloppant dans {}
bretelles:
print {$fh}<<EOT;
foo
EOT
la page de manuel perltrap répertorie de nombreux pièges pour les non avertis organisés par type.
le gotcha le plus commun est de démarrer vos fichiers avec quelque chose de différent de
use strict;
use diagnostics;
PJF ajoute: soyez averti que les diagnostics ont un impact significatif sur la performance. Il ralentit le démarrage du programme, il doit charger perldiag.pod, et jusqu'à bleadperl dès Il ya quelques semaines, il ralentit et gonfle regexps parce qu'il utilise $&. Il est recommandé d'utiliser les avertissements et d'exécuter splain
sur les résultats.
assigner des tableaux à des scalaires n'a aucun sens pour moi. Par exemple:
$foo = ( 'a', 'b', 'c' );
assigne ' c ' à $foo et jette le reste du tableau. Celui-ci est plus bizarre:
@foo = ( 'a', 'b', 'c' );
$foo = @foo;
cela ressemble à cela devrait faire la même chose que le premier exemple, mais au lieu de cela il place $foo
à la longueur de @foo
, donc $foo == 3
.
"mon" déclarations doivent utiliser des parenthèses autour de listes de variables
use strict;
my $a = 1;
mysub();
print "a is $a\n";
sub {
my $b, $a; # Gotcha!
$a = 2;
}
imprime 2 parce que le my
déclaration applique uniquement aux $b
(la mention de $a
sur cette ligne n'a tout simplement pas faire quelque chose). Notez que cela se produit sans avertissement même lorsque "use strict" est en vigueur.
ajouter "utiliser des avertissements" (ou le drapeau-w) améliore grandement les choses avec Perl dire parenthèses absentes autour de" ma "liste . Cela montre, comme beaucoup l'ont déjà fait, pourquoi le pragmatisme strict et les avertissements sont toujours une bonne idée.
Utilisation de la valeur non initialisée dans la concaténation...
celui-ci me rend fou. Vous avez une impression qui inclut un certain nombre de variables, comme:
print "$label: $field1, $field2, $field3\n";
et l'une des variables est undef . Vous considérez cela comme un bug dans votre programme -- c'est pourquoi vous utilisiez le "strict" pragma. Peut-être votre schéma de base de données permettait NULL dans un champ auquel vous ne vous attendiez pas, ou vous avez oublié d'initialiser une variable, etc. Mais tous les un message d'erreur vous indique qu'une valeur non initialisée a été rencontrée lors d'une opération de concaténation ( .
). Si seulement il vous a dit le nom de la variable qui a été uninitialized!
puisque Perl ne veut pas imprimer le nom de la variable dans le message d'erreur pour une raison quelconque, vous finissez par le traquer en définissant un point de rupture (pour regarder quelle variable est undef ), ou en ajoutant du code pour vérifier la condition. Très agaçant quand ça n'arrive qu'une fois sur des milliers dans un script CGI et que vous ne pouvez pas le recréer facilement.
Confusion des références et des objets réels:
$a = [1,2,3,4];
print $a[0];
(Il devrait être l'un des $a->[0]
(best), $$a[0]
, @{$a}[0]
ou @$a[0]
)
la plupart des opérateurs de boucle de Perl ( foreach
, map
, grep
) localisez automatiquement $_
mais while(<FH>)
ne le fait pas. Cela peut conduire à des actions étranges-à-distance.
je l'ai fait une fois:
my $object = new Some::Random::Class->new;
m'a Pris âges pour trouver l'erreur. La syntaxe de la méthode indirecte est eeevil .
my $x = <>;
do {
next if $x !~ /TODO\s*[:-]/;
...
} while ( $x );
do
n'est pas une boucle. Vous ne pouvez pas next
. C'est une instruction pour effectuer un bloc. C'est la même chose que
$inc++ while <>;
malgré qu'il ressemble à une construction dans la famille C des langues.
peuvent être redéfinies. Une façon simple de accidentellement redéfinir une constante est de définir une constante comme une référence.
use constant FOO => { bar => 1 };
...
my $hash = FOO;
...
$hash->{bar} = 2;
Maintenant FOO {bar => 2};
si vous utilisez mod_perl (au moins en 1.3), la nouvelle valeur FOO persistera jusqu'à ce que le module soit mis à jour.
Unaire moins avec "foo"
crée "-foo"
:
perl -le 'print -"foo" eq "-foo" ? "true" : "false"'
Cela ne fonctionne que si le premier caractère correspond à /[_a-zA-Z]/
. Si le premier caractère est un "-"
alors il modifie le premier caractère d'un "+"
, et si le premier caractère est un "+"
alors il modifie le premier caractère d'un "-"
. Si le premier caractère correspond à /[^-+_a-zA-Z]/
puis il tente de convertir la chaîne en nombre et nie le résultat.
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
'
le code ci-dessus imprime
-foo
+foo
-foo
-0
-5
cette fonctionnalité existe surtout pour permettre aux gens de dire des choses comme
my %options = (
-depth => 5,
-width => 2,
-height => 3,
);
Cette chasse aux sorcières est fixé en Perl 5.10 - si vous êtes assez chanceux pour travailler quelque part qui n'est pas allergique à la mise à niveau des choses >:-(
je parle de la Variable qui est valablement Zéro. vous savez, celui qui provoque des résultats inattendus dans des clauses comme:
unless ($x) { ... }
$x ||= do { ... };
Perl 5.10 A l'opérateur //= ou défini-ou .
c'est particulièrement insidieux quand le zéro valide est causé par un État de bord qui n'a pas été pris en compte dans les tests avant que votre code ne soit mis en production...
quelles valeurs prévoyez-vous que @ _ contienne dans le scénario suivant?
sub foo { }
# empty subroutine called in parameters
bar( foo(), "The second parameter." ) ;
je m'attends à recevoir dans bar :
undef, "The second parameter."
mais @ _ contient seulement le second paramètre, au moins lors d'un essai avec perl 5.88.
utilisant le modificateur /o
avec un motif regex stocké dans une variable.
m/$pattern/o
spécifier /o
est une promesse que $pattern
ne changera pas. Perl est assez intelligent pour reconnaître si oui ou non il a changé et recompiler le regex conditionnellement, donc il n'y a plus de bonne raison d'utiliser /o
. Alternativement, vous pouvez utiliser qr//
(par exemple si vous êtes obsédé par le fait d'éviter le chèque).
la réponse de Graeme Perrow était bonne, mais elle est encore meilleure!
étant donné une fonction typique qui renvoie une belle liste dans le contexte d'une liste, vous pourriez bien vous demander: Qu'est-ce qu'elle retournera dans le contexte scalaire? (Par "typique", je veux dire le cas commun dans lequel la documentation ne dit pas, et nous supposons qu'il n'utilise aucune wantarray
Drôle d'affaire. C'est peut-être une fonction que vous avez écrite vous-même.)
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
le contexte une fonction appelée est propagée dans les instructions return
de cette fonction.
je suppose que l'appelant avait tort de vouloir une simple règle empirique pour permettre un raisonnement efficace sur le comportement code . Vous avez raison, Perl, il est préférable pour le personnage de l'appelant à ramper à travers le code source de la fonction appelée à chaque fois .
comparant les cordes en utilisant ==
et !=
au lieu de eq
et ne
. Par exemple:
$x = "abc";
if ($x == "abc") {
# do something
}
au lieu de:
$x = "abc";
if ($x eq "abc") {
# do something
}
Que pensez-vous du fait que
@array = split( / /, $string );
ne donne pas le même résultat que
@array = split( ' ', $string );
si $ string a des espaces principaux?
qui pourrait surprendre certaines personnes.
vous ne pouvez pas localiser les variables exportées à moins d'exporter la totalité de typeglob.
Si vous êtes assez stupide pour le faire Perl vous permettra de déclarer plusieurs variables avec le même nom:
my ($x, @x, %x);
parce que Perl utilise des sigles pour identifier context plutôt que la variable type , cela garantit presque la confusion quand le code plus tard utilise les variables, en particulier si $x
est une référence:
$x[0]
$x{key}
$x->[0]
$x->{key}
@x[0,1]
@x{'foo', 'bar'}
@$x[0,1]
@$x{'foo', 'bar'}
...
ajouter des parenthèses supplémentaires ne pourrait jamais changer le sens du code , Non? Droit?
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, c'est vrai, C'est Perl.
EDIT: pour faire bonne mesure, si x
est appelée dans un contexte scalaire, puis les parenthèses n'a pas d'importance, après tout:
my $z = ( "A" x 5 ); # $z contains "AAAAA"
my $w = (("A") x 5 ); # $w contains "AAAAA" too
Intuitive.
oubliant de préparer le chemin du répertoire aux résultats de readdir
avant de faire des tests sur ces résultats. Voici un exemple:
#!/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...
Cette chasse aux sorcières est mentionné dans la documentation de la readdir
, mais je pense que c'est encore une erreur assez commune.
hachage "constructeur" n'est rien de plus qu'une liste, et la =>
fat virgule est rien de plus que du sucre syntaxique. Vous pouvez vous faire mordre par ceci en confondant la syntaxe []
avec la syntaxe ()
de la liste:
my %var = (
("bar", "baz"),
fred => "barney",
foo => (42, 95, 22)
);
# result
{ 'bar' => 'baz',
'95' => 22,
'foo' => 42,
'fred' => 'barney' };
# wanted
{ 'foo' => [ 42, 95, 22 ] }
fautes d'Orthographe dans les noms de variables ... Une fois, j'ai passé un après-midi entier à résoudre un code qui ne se comportait pas correctement seulement pour trouver une faute de frappe sur un nom de variable, ce qui n'est pas une erreur dans Perl, mais plutôt la déclaration d'une nouvelle variable.
modifiant le tableau sur lequel vous faites une boucle dans un pour(chacun) comme dans:
my @array = qw/a b c d e f g h/;
for ( @array ) {
my $val = shift @array;
print $val, "\n";
}
il devient confus et ne fait pas ce que vous attendez
traiter un scalaire comme un entier:
$n = 1729;
$s = 0;
$i = 0;
while ($n) {
$s += $n % 10;
$n/=10;
$i ++
}
print "Sum : $s\n";
print "Number of iterations : $i\n"
somme: 19
nombre d'itérations : 327
Idéalement, il devrait avoir eu seulement quatre itérations, mais un scalaire n'est pas un int, et nous avons obtenu un résultat inattendu.