Objets courants pour Perl?

La question sur les fonctionnalités cachées de Perl a généré au moins une réponse qui peut être considérée comme une fonctionnalité ou une erreur. Il semblait logique de donner suite à cette question: quelles sont les erreurs courantes non évidentes dans Perl? Des choses qui semblent devoir fonctionner, mais pas.

Je ne donnerai pas de directives sur la façon de structurer les réponses, ou sur ce qui est “trop ​​facile” à considérer comme un piège, puisque c’est le but du vote.

Table de Réponses

Syntaxe

  • Général
    • Citations simples au lieu de :: dans les identificateurs
    • Syntaxe indirecte des objects
    • Des références déroutantes avec des types var simples
  • Poignées de fichiers
    • Notation Heredoc lors de l’utilisation d’imprimés avec des descripteurs lexicaux
    • Impression sur un descripteur lexical contenu dans un hachage
    • my déclarations doivent utiliser parens autour des listes de variables
    • Comparer des chaînes avec == et! =

Sémantique / Langue

  • Général
    • do n’est pas une boucle. Vous ne pouvez pas next .
    • Utiliser le modificateur / o avec une regex
    • Oublier que les résultats de readdir ne sont pas relatifs à la MDC
    • Interaction moins unaire avec les chaînes
  • Le contexte
    • Affectation à un scalaire à partir de tableaux ou de listes
    • L’iterator glob () (sur une autre question)
    • Retours implicites dans le contexte de la liste
    • La parenthèse change la sémantique des opérateurs
    • Le contexte d’appel est propagé pour renvoyer des instructions au sein de fonctions
  • Les variables
    • Impossible de localiser les variables exscopes sans exporter le typeglob complet
    • Utiliser plusieurs variables (de différents types) avec le même nom
    • while ne localise pas automatiquement $_
    • La variable qui vaut valablement zéro
    • Les constantes peuvent être redéfinies

Le débogage

  • Avertissement: utilisation de la valeur non initialisée dans la concaténation

Les meilleures pratiques

  • Oublier d’ use ssortingct use warnings use ssortingct et use warnings (ou use diagnostics )
  • Noms de variables mal orthographiés (c.-à-d., use ssortingct nouveau use ssortingct )

Méta-Réponses

  • La page de manuel perltrap
  • Perl :: Critique

Voir aussi: ASP.NET – Common getchas

Le fait que des guillemets simples puissent être utilisés pour remplacer :: dans les identifiants.

Considérer:

 use ssortingct; print "$foo"; #-- Won't comstack under use ssortingct print "$foo's fun!"; #-- Comstacks just fine, refers to $foo::s 

Menant au problème suivant:

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

La méthode recommandée pour éviter cela consiste à utiliser des accolades autour de votre nom de variable:

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

Et aussi pour use warnings , car cela vous dira à propos de l’utilisation de la variable non définie $name::s

Vous pouvez imprimer sur un descripteur lexical: bon.

 print $out "hello, world\n"; 

Vous vous rendez compte alors que cela pourrait être bien d’avoir un hachage de descripteurs de fichiers:

 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 sur l’une ou l’autre selon une condition:

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

Vous devez envelopper le déréférencement dans une paire de boucles:

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

C’est un comportement complètement non intuitif. Si vous n’en avez pas entendu parler ou que vous ne l’avez pas lu dans la documentation, je doute que vous puissiez vous débrouiller tout seul.

Ceci est une méta-réponse. 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 sans pouvoir personnaliser votre options) via le site Perl :: Critic .

Perl::Critic fournit également des références au livre des meilleures pratiques Perl de Damian Conways, y compris les numéros de page. Donc, si vous êtes trop paresseux pour lire le livre en entier, Perl::Critic peut toujours vous dire ce que vous devriez lire.

DWIMmer de Perl a du mal avec la notation << (ici-document) lors de l'utilisation d' print avec des descripteurs lexicaux:

 # here-doc print $fh < 

La solution consiste soit à inclure des espaces entre le descripteur de fichier et le << ou à désambiguïser le descripteur de fichier en l’encadrant entre accolades {} :

 print {$fh}< 

La page de manuel perltrap répertorie de nombreux pièges pour les imprudents organisés par type.

L’affectation de tableaux à des scalaires n’a aucun sens pour moi. Par exemple:

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

Assigne «c» à $ foo et jette le rest du tableau. Celui-ci est plus étrange:

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

Cela semble devoir faire la même chose que le premier exemple, mais place $foo à la longueur de @foo , donc $foo == 3 .

Le plus commun est de démarrer vos fichiers avec autre chose que

 use ssortingct; use diagnostics; 

pjf ajoute: Veuillez noter que les diagnostics ont un impact significatif sur les performances. Il ralentit le démarrage du programme, car il doit charger perldiag.pod, et jusqu’à bleadperl il y a quelques semaines, il ralentit et gonfle également les regexps car il utilise $ &. Il est recommandé d’utiliser des avertissements et d’exécuter splain sur les résultats.

Références déroutantes et objects réels:

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

(Ce devrait être un de $a->[0] (meilleur), $$a[0] , @{$a}[0] ou @$a[0] )

“mes” déclarations devraient utiliser des parenthèses autour des listes de variables

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

Il imprime a est 2 parce que my déclaration s’applique uniquement à $b (la mention de $a sur cette ligne n’a tout simplement rien fait). Notez que cela se produit sans avertissement même lorsque “use ssortingct” est en vigueur.

L’ajout de “use warnings” (ou du drapeau -w) améliore grandement les choses avec Perl disant que les parenthèses sont manquantes autour de “ma” liste . Cela montre, comme beaucoup d’autres l’ont déjà fait, pourquoi les pragmas ssortingcts 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 comprend 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 bogue dans votre programme – c’est pourquoi vous utilisiez le pragma “ssortingct”. Peut-être que votre schéma de firebase database permettait NULL dans un champ inattendu ou vous avez oublié d’initialiser une variable, etc. Mais tout le 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 indiquait le nom de la variable non initialisée!

Puisque Perl ne veut pas imprimer le nom de la variable dans le message d’erreur pour une raison quelconque, vous finissez par le suivre en définissant un point d’arrêt (pour voir quelle variable est undef ) ou en ajoutant du code pour vérifier la condition. Très ennuyeux quand cela arrive seulement une fois sur des milliers dans un script CGI et que vous ne pouvez pas le recréer facilement.

La plupart des opérateurs de boucle de Perl ( foreach , map , grep ) localisent automatiquement $_ mais while() ne le fait pas. Cela peut conduire à une action étrange à distance.

Je l’ai fait une fois:

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

Il m’a fallu des â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 blocage. C’est la même chose que

 $inc++ while <>; 

Malgré cela, cela ressemble à une construction dans la famille C des langues.

Les constantes peuvent être redéfinies. Un moyen simple de redéfinir accidentellement une constante consiste à définir une constante comme référence.

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

Maintenant, FOO est {bar => 2};

Si vous utilisez mod_perl (au moins en 1.3), la nouvelle valeur FOO persistera jusqu’à ce que le module soit actualisé.

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’attendrais à recevoir en bar :

 undef, "The second parameter." 

Mais @_ ne contient que le second paramètre, au moins lors du test avec le perl 5.88.

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 "-" , le premier caractère devient un "+" et si le premier caractère est un "+" , le premier caractère devient un "-" . Si le premier caractère correspond à /[^-+_a-zA-Z]/ alors il tente de convertir la chaîne en nombre et annule 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 s’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, ); 

Ce gotcha est corrigé dans Perl 5.10 – si vous avez la chance de travailler quelque part qui n’est pas allergique à la mise à niveau des choses> 🙁

Je parle de la variable Validly Zero. 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 .

Ceci est particulièrement insidieux lorsque le zéro valide est provoqué par une condition de bord qui n’a pas été prise en compte dans les tests avant que votre code ne soit passé en production …

La réponse de Graeme Perrow était bonne, mais ça va encore mieux!

Étant donné une fonction typique qui retourne une belle liste dans un contexte de liste, vous pourriez vous demander: Qu’est-ce que cela retournera dans un contexte scalaire? (Par “typique”, je veux dire le cas commun dans lequel la documentation ne dit pas, et nous supposons qu’elle n’utilise aucune wantarray amusante. Peut-être que c’est 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 dans lequel une fonction est appelée est propagé pour return instructions dans cette fonction.

Je suppose que l’appelant a eu tort de vouloir une règle simple pour permettre un raisonnement efficace sur le comportement du code . Vous avez raison, Perl, il est préférable que le caractère de l’appelant passe à travers le code source de la fonction appelée à chaque fois .

Vous ne pouvez pas localiser les variables exscopes à moins d’exporter l’intégralité du typeglob.

Utilisation du 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 recomstackr la regex de manière conditionnelle, il n’y a donc plus de raison d’utiliser /o . Alternativement, vous pouvez utiliser qr// (par exemple, si vous êtes obsédé par la vérification du contrôle).

Comparer des chaînes en utilisant == et != 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 diriez-vous du fait que

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

ne donne pas le même résultat que

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

si $ ssortingng a des espaces en tête?

Cela pourrait prendre des gens par surprise.

L’ajout de parenthèses supplémentaires ne pourrait jamais changer la signification du code , non? Droite?

 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: Juste pour faire bonne mesure, si x est appelé dans un contexte scalaire, les parenthèses ne comptent pas après tout:

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

Intuitif.

Si vous êtes assez stupide pour le faire, Perl vous permettra de déclarer plusieurs variables portant le même nom:

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

Étant donné que Perl utilise des sigils pour identifier le contexte plutôt que le type de variable, cela garantit quasiment la confusion lorsque du code utilise les variables ultérieurement, 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'} ... 

Oublier d’append 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 ssortingct; 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... 

Ce gotcha est mentionné dans la documentation de readdir , mais je pense que c’est toujours une erreur assez courante.

Le hachage “constructeur” n’est rien de plus qu’une liste, et la virgule => gras n’est rien de plus que du sucre syntaxique. Vous pouvez être mordu par ceci en confondant la syntaxe de arrayref [] avec la syntaxe de 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 ] } 

Noms de variables mal orthographiés … J’ai passé une après-midi entière à résoudre le problème de code qui ne se comportait pas correctement pour trouver une faute de frappe sur un nom de variable, ce qui n’est pas une erreur dans Perl, mais une déclaration.

Modifier le tableau que vous mettez en boucle dans un pour (chacun) comme dans:

 my @array = qw/abcdefgh/; for ( @array ) { my $val = shift @array; print $val, "\n"; } 

il est 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 ne devrait avoir que quatre itérations, mais un scalaire n’est pas un int et nous avons un résultat inattendu.