NOM
perlfaq7 - Problèmes du langage Perl ($Revision: 1.28 $, $Date: 1999/05/23 20:36:18 $)
DESCRIPTION
Cette section traite de problèmes généraux du langage Perl qui ne trouvent leur place dans aucune autre section.
Puis-je avoir une BNF/yacc/RE pour le langage Perl ?
Il n'y a pas de BNF, mais vous pouvez vous frayer un chemin dans la grammaire de yacc dans perly.y au sein de la distribution source si vous êtes particulièrement brave. La grammaire repose sur un code de mots-clés complexe, préparez-vous donc à vous aventurer aussi dans toke.c.
Selon les termes de Chaim Frenkel : "La grammaire de Perl ne peut pas être réduite à une BNF. Le travail d'analyse de perl est distribué entre yacc, l'analyser lexical, de la fumée et des miroirs".
Quels sont tous ces $@%&* de signes de ponctuation, et comment savoir quand les utiliser ?
Ce sont des spécificateurs, comme détaillé dans perldata :
$ pour les valeurs scalaires (nombres, chaînes ou références)
@ pour les tableaux
% pour les hachages (tableaux associatifs)
& pour les sous-programmes (alias les fonctions, les procédures,
les méthodes)
* pour tous les types de la table des noms courante. Dans la
version 4, vous les utilisiez comme des pointeurs, mais dans les
versions modernes de perl, vous pouvez utiliser simplement les
références.
Quelques autre symboles que vous risquez fortement de rencontrer sans qu'ils soient véritablement des spécificateurs de type sont :
<> utilisés pour récupérer un enregistrement depuis un
filehandle.
\ prend une référence à quelque chose.
Notez que <FICHIER> n'est ni le spécificateur de type pour les fichiers ni le nom du handle. C'est l'opérateur <>
appliqué au handle FICHIER. Il lit une ligne (en fait un enregistrement - voir "$/" in perlvar) depuis le handle FICHIER dans un contexte scalaire, ou toutes les lignes dans un contexte de liste. Lorsqu'on ouvre, ferme ou réalise n'importe quelle autre opération à part <>
sur des fichiers, ou même si l'on parle du handle, n'utilisez pas les crochets. Ces expressions sont correctes : eof(FH)
, seek(FH, 0, 2)
et "copie de STDIN vers FICHIER".
Dois-je toujours/jamais mettre mes chaînes entre guillemets ou utiliser les points-virgules et les virgules ?
Normalement, un bareword n'a pas besoin d'être mis entre guillemets, mais dans la plupart des cas, il le devrait probablement (et doit être sous use strict
). Mais une clé de hachage constituée d'un simple mot (qui ne soit pas le nom d'un sous-programme) et l'opérande de gauche de l'opérateur =>
, comptent tous les deux comme s'ils étaient entre guillemets :
Ceci équivaut à ceci
------------ ---------------
$foo{line} $foo{"line"}
bar => stuff "bar" => stuff
Le point-virgule final dans un bloc est optionnel, tout comme la virgule finale dans une liste. Un bon style (voir perlstyle) préconise de les mettre sauf dans les expressions d'une seule ligne :
if ($whoops) { exit 1 }
@nums = (1, 2, 3);
if ($whoops) {
exit 1;
}
@lines = (
"There Beren came from mountains cold",
"And lost he wandered under leaves",
);
Comment ignorer certaines valeurs de retour ?
Une façon est de traiter les valeurs de retour comme une liste et de les indexer :
$dir = (getpwnam($user))[7];
Une autre façon est d'utiliser undef comme un élément de la partie gauche :
($dev, $ino, undef, undef, $uid, $gid) = stat($file);
Comment bloquer temporairement les avertissements ?
Si vous utilisez Perl 5.6.0 ou supérieur, le pragma use warnings
vous permet un contrôle fin des avertissements qui sont générés. Voir perllexwarn pour plus de détails.
{
no warnings; # désactive temporairement les avertissements
$a = $b + $c; # je sais qu'elles peuvent être indéfinies
}
Si vous avez une version plus ancienne de Perl, la variable $^W
(documentée dans perlvar) contrôle les avertissements lors de l'exécution d'un bloc :
{
local $^W = 0; # supprimer temporairement les
avertissements
$a = $b + $c; # je sais que ces variables sont
peut-être indéfinies
}
Notez que comme toutes les variables de ponctuation, vous ne pouvez pas utiliser my() sur $^W
, uniquement local().
Qu'est-ce qu'une extension ?
Une façon d'appeler du code C compilé depuis Perl. Lire perlxstut est une bonne façon d'en apprendre plus sur les extensions.
Pourquoi les opérateurs de Perl ont-ils une précédence différente de celle des opérateurs en C ?
En fait, ce n'est pas le cas. Tous les opérateurs C que Perl copie ont la même précédence en Perl qu'en C. Le problème est avec les opérateurs que C ne possède pas, en particulier les fonctions qui donnent un contexte de liste à tout ce qui se trouve à leur droite, par exemple print, chmod, exec, et ainsi de suite. De telles fonctions sont appelées des "opérateurs de liste" et apparaissent en tant que telles dans la table de précédence de perlop.
Une erreur commune est d'écrire :
unlink $file || die "snafu";
Qui est interprété ainsi :
unlink ($file || die "snafu");
Pour éviter ce problème, placez soit des parenthèses supplémentaires ou utilisez l'opérateur de précédence super basse or
:
(unlink $file) || die "snafu";
unlink $file or die "snafu";
Les opérateurs "anglais" (and
, or
, xor
, et not
) ont une précédence délibérément plus basse que celle des opérateurs de liste juste pour des situations telles que ci-dessus.
Un autre opérateur ayant une précédence surprenante est l'exponentiation. Il se lie encore plus étroitement que même le moins unaire, faisant du produit -2**2
un quatre négatif et non pas positif. Il s'associe aussi à droite, ce qui signifie que 2**3**2
vaut deux à la puissance neuvième, et non pas huit au carré.
Bien qu'il ait la même précédence qu'en C, l'opérateur ?:
de Perl produit une lvalue. Ce qui suit affecte $x soit à $a soit à $b, selon la valeur de vérité de $maybe :
($maybe ? $a : $b) = $x;
Comment déclarer/créer une structure ?
En général, vous ne "déclarez" pas une structure. Utilisez juste une référence de hachage (probablement anonyme). Voir perlref et perldsc pour plus de détails. Voici un exemple :
$person = {}; # nouveau hachage anonyme
$person->{AGE} = 24; # fixe le champ AGE à 24
$person->{NAME} = "Nat"; # fixe le champ NAME à "Nat"
Si vous recherchez quelque chose d'un peu plus rigoureux, essayez perltoot.
Comment créer un module ?
Un module est un paquetage qui vit dans un fichier du même nom. Par exemple, le module Hello::There vivrait dans Hello/There.pm. Pour plus de détails, lisez perlmod. Vous trouverez aussi Exporter d'une grande aide. Si vous écrivez un module en C ou utilisant à la fois C et Perl, alors vous devriez étudier perlxstut.
Voici un modèle pratique que vous pourriez vouloir utiliser pour débuter votre propre module. Assurez-vous de changer les noms de façon appropriée.
package Some::Module; # présume Some/Module.pm
use strict;
use warnings;
BEGIN {
use Exporter ();
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
## définit la version pour vérification ; décommentez pour
## l'utiliser
## $VERSION = 1.00;
# si vous utilisez RCS/CVS, la ligne suivante serait peut-être
# préférable, mais attention aux numéros de version à deux
# chiffres.
$VERSION = do{my@r=q$Revision: 1.28 $=~/\d+/g;sprintf '%d.'.'%02d'x$#r,@r};
@ISA = qw(Exporter);
@EXPORT = qw(&func1 &func2 &func3);
%EXPORT_TAGS = ( ); # eg: TAG => [ qw!name1 name2! ],
# les variables globales exportées de votre paquetage vont
# ici, ainsi que toute fonction exportée optionnelle
@EXPORT_OK = qw($Var1 %Hashit);
}
our @EXPORT_OK;
# les variables globales non exportées du paquetage vont ici
our @more;
our $stuff;
# initialisation des variables globales du paquetage, d'abord
# celles qui sont exportées
$Var1 = '';
%Hashit = ();
# puis les autres (qui sont toujours accessibles par $Some::Module::stuff)
$stuff = '';
@more = ();
# tous les lexicaux ayant le fichier pour portée doivent être
# créés avant les fonctions qui les utilisent.
# les lexicaux privés aux fichiers vont ici
my $priv_var = '';
my %secret_hash = ();
# voici une fonction privée au fichier en tant que fermeture,
# appelable en tant que &$priv_func; elle ne peut pas être
# prototypée.
my $priv_func = sub {
# des trucs ici.
};
# créez toutes vos fonctions, qu'elles soient exportées ou non ;
# souvenez-vous de mettre quelque chose d'intéressant dans les
# parties entre {}
sub func1 {} # pas de prototype
sub func2() {} # prototypée vide
sub func3($$) {} # prototypée avec deux scalaires
# celle-ci n'est pas exportée, mais pourrait être appelée !
sub func4(\%) {} # prototypée avec une référence de hachage
END { } # ici, code de nettoyage du module (destructeur
# global)
1; # les modules doivent renvoyer vrai
Le programme h2xs créera pour vous les stubs pour tous les trucs importants :
% h2xs -XA -n My::Module
Comment créer une classe ?
Voir perltoot pour une introduction aux classes et aux objets, ainsi que perlobj et perlbot.
Comment déterminer si une variable est souillée ?
Voir "Blanchissage et Détection des Données Souillées" in perlsec. Voici un exemple (qui n'utilise aucun appel système, car aucun processus auquel envoyer le signal n'est passé au kill() ) :
sub is_tainted {
return ! eval { join('',@_), kill 0; 1; };
}
Ceci n'est toutefois pas propre pour -w
. Il n'y a pas de façon propre selon le sens de -w
pour détecter une souillure - prenez ceci comme un indice indiquant que vous devriez nettoyer toutes vos données pouvant être souillées.
Qu'est-ce qu'une fermeture ?
Les fermetures sont documentées dans perlref.
La Fermeture est un terme d'informatique ayant un sens précis mais difficile à expliquer. Les fermetures sont implémentées en Perl par des sous-programmes anonymes ayant des références à des variables lexicales qui persistent hors de leur propre portées. Ces lexicaux se réfèrent de façon magique aux variables qui se trouvaient dans le coin lorsque le sous-programme a été défini (deep binding).
Les fermetures ont un sens dans tous les langages de programmation où la valeur de retour d'une fonction peut être une fonction, comme c'est le cas en Perl. Notez que certains langages fournissent des fonctions anonymes sans être capables de fournir des fermetures proprement dites ; le langage Python, par exemple. Pour plus d'informations sur les fermetures, regardez dans un livre sur la programmation fonctionnelle. Scheme est un langage qui non seulement supporte mais aussi encourage les fermetures.
Voici une fonction classique de génération de fonctions :
sub add_function_generator {
return sub { shift + shift };
}
$add_sub = add_function_generator();
$sum = $add_sub->(4,5); # $sum vaut maintenant 9.
La fermeture fonctionne comme un modèle de fonction ayant des possibilités de personnalisation qui seront définies plus tard. Le sous-programme anonyme retourné par add_function_generator() n'est pas techniquement une fermeture car il ne se réfère à aucun lexical hors de sa propre portée.
Comparez cela à la fonction suivante, make_adder(), dans laquelle la fonction anonyme retournée contient une référence à une variable lexicale se trouvant hors de la portée de la fonction elle-mêeme. Une telle référence nécessite que Perl renvoie une fermeture proprement dite, verrouillant ainsi pour toujours la valeur que le lexical avait lorsque la fonction fut créé.
sub make_adder {
my $addpiece = shift;
return sub { shift + $addpiece };
}
$f1 = make_adder(20);
$f2 = make_adder(555);
Maintenant &$f1($n)
vaut toujours 20 plus la valeur de $n que vous lui passez, tandis que &$f2($n)
vaut toujours 555 plus ce qui se trouve dans $n. La variable $addpiece dans la fermeture persiste.
Les fermetures sont souvent utilisées pour des motifs moins ésotériques. Par exemple, lorsque vous désirez passer un morceau de code à une fonction :
my $line;
timeout( 30, sub { $line = <STDIN> } );
Si le code à exécuter a été passé en tant que chaîne, '$line = <STDIN>'
, la fonction hypothétique timeout() n'aurait aucun moyen d'accéder à la variable lexicale $line dans la portée de son appelant.
Qu'est-ce que le suicide de variable et comment le prévenir ?
Le suicide de variable se produit lorsque vous perdez la valeur d'une variable (temporairement ou de façon permanente). Il est causé par les interactions entre la portée définie par my() et local() et soit des fermetures soit des variables d'itération foreach() aliasées et des arguments de sous-programme (Note au relecteur : un peu bordélique, cette phrase). Il a été facile de perdre par inadvertance la valeur d'une variable de cette façon, mais désormais c'est bien plus difficile. Considérez ce code :
my $f = "foo";
sub T {
while ($i++ < 3) { my $f = $f; $f .= "bar"; print $f, "\n" }
}
T;
print "Finally $f\n";
Le $f qui se voit ajouter "bar" par trois fois devrait être un nouveau $f
(my $f
devrait créer une nouvelle variable locale chaque fois que la boucle est parcourue). Toutefois, ce n'est pas le cas. C'était un bug, désormais corrigé dans les dernières versions (testé avec 5.004_05, 5.005_03 et 5.005_56).
Comment passer/renvoyer {une fonction, un handle de fichier, un tableau, un hachage, une méthode, une expression rationnelle} ?
À l'exception des expressions rationnelles, vous devez passer des références à ces objets. Voir "Passage par Référence" in perlsub pour cette question particulière, et perlref pour plus d'informations sur les références.
- Passer des Variables et des Functions
-
Les variables normales et les fonctions sont faciles à passer : passez juste une référence à une variable ou une fonction, existante ou anonyme :
func( \$some_scalar ); func( \@some_array ); func( [ 1 .. 10 ] ); func( \%some_hash ); func( { this => 10, that => 20 } ); func( \&some_func ); func( sub { $_[0] ** $_[1] } );
- Passer des Handles de Fichier
-
Pour passer des handles de fichier à des sous-programmes, utilisez les notations
*FH
ou\*FH
. Ce sont des "typeglobs" - voir "Typeglobs et Handles de Fichiers" in perldata et en particulier "Passage par Référence" in perlsub pour plus d'information.Voici un extrait :
Si vous passez des handles de fichier, vous pouvez habituellement simplement utiliser le typeglob seul, comme *STDOUT, mais les références aux typeglobs seraient une meilleure solution parce qu'elles fonctionnent encore correctement sous
use strict 'refs'
. Par exemple :splutter(\*STDOUT); sub splutter { my $fh = shift; print $fh "her um well a hmmm\n"; } $rec = get_rec(\*STDIN); sub get_rec { my $fh = shift; return scalar <$fh>; }
Si vous avez l'intention de générer de nouveau handles de fichier, vous pourriez faire ceci :
sub openit { my $name = shift; local *FH; return open (FH, $path) ? *FH : undef; } $fh = openit('< /etc/motd'); print <$fh>;
- Passer des Expressions Rationnelles
-
Pour passer des expressions rationnelles, vous devrez utiliser une version de Perl suffisamment récente pour supporter la construction
qr//
, passer des chaînes et utiliser un eval capturant les exceptions, soit encore vous montrer très très intelligent.Voici un exemple montrant comment passer une chaîne devant être comparée comme expression rationnelle :
sub compare($$) { my ($val1, $regex) = @_; my $retval = $val1 =~ /$regex/; return $retval; } $match = compare("old McDonald", qr/d.*D/i);
Notez comment
qr//
autorise les drapeaux terminaux. Ce motif a été compilé à la compilation, même s'il n'a été exécuté que plus tard. La sympathique notationqr//
n'a pas été introduite avant la version 5.005. Auparavant, vous deviez approcher ce problème bien moins intuitivement. La revoici par exemple si vous n'avez pasqr//
:sub compare($$) { my ($val1, $regex) = @_; my $retval = eval { $val1 =~ /$regex/ }; die if $@; return $retval; } $match = compare("old McDonald", q/($?i)d.*D/);
Assurez-vous de ne jamais dire quelque chose comme ceci :
return eval "\$val =~ /$regex/"; # FAUX
ou quelqu'un pourrait glisser des séquences d'échappement du shell dans l'expressions rationnelle à cause de la double interpolation de l'eval et de la chaîne doublement mise entre guillemets. Par exemple :
$pattern_of_evil = 'danger ${ system("rm -rf * &") } danger'; eval "\$string =~ /$pattern_of_evil/";
Ceux qui préfèrent être très très intelligents peuvent voir le livre O'Reilly Mastering Regular Expressions, par Jeffrey Friedl. La fonction Build_MatchMany_Function() à la page 273 est particulièrement intéressante. Une citation complète de ce livre est donnée dans perlfaq2.
- Passer des Méthodes
-
Pour passer une méthode objet à un sous-programme, vous pouvez faire ceci :
call_a_lot(10, $some_obj, "methname") sub call_a_lot { my ($count, $widget, $trick) = @_; for (my $i = 0; $i < $count; $i++) { $widget->$trick(); } }
Ou bien vous pouvez utiliser une fermeture pour encapsuler l'objet, son appel de méthode et ses arguments :
my $whatnot = sub { $some_obj->obfuscate(@args) }; func($whatnot); sub func { my $code = shift; &$code(); }
Vous pourriez aussi étudier la méthode can() de la classe UNIVERSAL (qui fait partie de la distribution standard de perl).
Comment créer une variable statique ?
Comme pour la plupart des choses en Perl, TMTOWTDI ["There is More Than One Way To Do It", "il y a plus d'une façon de le faire", NDT]. Ce qu'on appelle une "variable statique" dans d'autres langages pourrait être soit une variable privée à une fonction (visible seulement à l'intérieur d'une seule fonction, et gardant sa valeur entre deux appels à cette fonction), soit une variable privée à un fichier (visible seulement par les fonctions se trouvant dans le fichier dans laquelle elle a été déclarée), en Perl.
Voici du code qui implémente une variable privée à une fonction :
BEGIN {
my $counter = 42;
sub prev_counter { return --$counter }
sub next_counter { return $counter++ }
}
Désormais, prev_counter() et next_counter() partagent une variable privée $counter qui a été initialisée lors de la compilation.
Pour déclarer une variable privée à un fichier, vous utiliserez encore un my(), en le mettant en haut du fichier, pour qu'il ait le plus grand niveau de portée. En supposant que ceci se trouve dans le fichier Pax.pm :
package Pax;
my $started = scalar(localtime(time()));
sub begun { return $started }
Lorsque use Pax
ou require Pax
chargeront ce module, la variable sera initialisée. Elle ne sera pas supprimée à la façon dont la plupart des variables qui sortent de la portée le sont, car la fonction begun() s'en occupe, mais personne d'autre ne peut l'atteindre. Elle n'est pas appelée $Pax::started parce que sa portée n'a pas de relation avec le paquetage. Sa portée est celle du fichier. Il est concevable que vous ayez plusieurs paquetage dans ce même fichier, accédant tous à la même variable privée, mais un autre fichier contenant le même paquetage ne pourrait pas l'atteindre.
Voir "Variables Privées Persistantes" in perlsub pour plus de détails.
Quelle est la différence entre la portée dynamique et lexicale (statique) ? Entre local() et my() ?
local($x)
sauvegarde l'ancienne valeur de la variable globale $x
, et lui affecte une nouvelle valeur pour la durée du sous-programme, valeur qui est visible dans les autres fonctions appelées depuis ce sous-programme. C'est fait lors de l'exécution, et c'est donc appelé une portée dynamique. local() affecte toujours les variables globales, aussi appelées variables de paquetage ou variables dynamiques.
my($x)
crée une nouvelle variable qui n'est visible que dans le sous-programme. C'est fait lors de la compilation, et c'est donc appelé une portée lexicale ou statique. my() affecte toujours les variables privées, aussi appelées variables lexicales ou (de façon impropre) variables (de portée) statique.
Par exemple :
sub visible {
print "var has value $var\n";
}
sub dynamic {
local $var = 'local'; # nouvelle valeur temporaire pour la
visible(); # variable toujours globale appelée $var
}
sub lexical {
my $var = 'private'; # nouvelle variable privée, $var
visible(); # (invisible hors de la portée de la routine)
}
$var = 'global';
visible(); # affiche global
dynamic(); # affiche local
lexical(); # affiche global
Notez que, comme à aucun moment la valeur "private" n'est affichée. C'est parce que $var n'a cette valeur qu'à l'intérieur du bloc de la fonction lexical(), et est cachée des sous-programmes appelés.
En résumé, local() ne crée pas comme vous pourriez le penser des variables locales, privées. Il donne seulement à une variable globale une valeur temporaire. my() est ce que vous recherchez si vous voulez des variables privées.
Voir "Variables Privées via my()" in perlsub et "Valeurs Temporaires via local()" in perlsub pour des détails insoutenables.
Comment puis-je accéder à une variable dynamique lorsqu'un lexical de même nom est dans la portée ?
Vous pouvez le faire via des références symboliques, pourvu que vous n'ayez pas fixé use strict "refs"
. Donc, à la place de $var, utilisez ${'var'}
.
local $var = "global";
my $var = "lexical";
print "lexical is $var\n";
no strict 'refs';
print "global is ${'var'}\n";
Si vous connaissez votre paquetage, vous pouvez simplement le mentionner explicitement, comme dans $Some_Pack::var. Notez que la notation $::var n'est pas la $var dynamique du paquetage courant, mais au contraire celle du paquetage main
, comme si vous aviez écrit $main::var. Spécifier le paquetage directement vous fait hard-coder [au relecteur : hard-coder ?] son nom, mais cela s'exécute plus vite et vous permet d'éviter de vous attirer les foudres de use strict "refs"
.
Quelle est la différence entre les liaisons profondes et superficielles ?
Dans la liaison profonde, les variables lexicales mentionnées dans les sous-programmes anonymes sont les mêmes que celles qui étaient dans la portée lorsque le sous-programme fut créé. Dans la liaison superficielle, ce sont les variables, quelles qu'elles soient, ayant le même nom et s'étant trouvé dans la portée lorsque le sous-programme est appelé. Perl utilise toujours la liaison profonde des variables lexicales (i.e., celles créées avec my()). Toutefois, les variables dynamiques (aussi appelées globales, locales ou variables de paquetage) sont effectivement liées superficiellement. Considérez ceci comme simplement une raison de plus de ne pas les utiliser. Voir la réponse à "Qu'est-ce qu'une fermeture ?".
Pourquoi "my($foo) = <FICHIER>;" ne marche pas ?
my()
et local()
donnent un contexte de liste à la partie droite de =
. L'opération de lecture <FH>, comme tant d'autres fonctions et opérateurs de Perl, peut déterminer dans quel contexte elle a été appelée pour se comporter de façon appropriée. En général, la fonction scalar() peut aider. Cette fonction ne fait rien aux données elles-mêmes (contrairement au mythe populaire) mais dit seulement à ses arguments de se comporter à sa propre manière scalaire. Si cette fonction n'a pas de comportement scalaire défini, ceci ne vous aidera visiblement pas (tout comme avec sort()).
Pour forcer un contexte scalaire dans ce cas particulier, toutefois, vous devez tout simplement omettre les parenthèses :
local($foo) = <FILE>; # FAUX
local($foo) = scalar(<FILE>); # ok
local $foo = <FILE>; # bien
Vous devriez probablement utiliser des variables lexicales de toute façon, même si le résultat est le même ici :
my($foo) = <FILE>; # FAUX
my $foo = <FILE>; # bien
Comment redéfinir une fonction, un opérateur ou une méthode prédéfini ?
Pourquoi donc voulez-vous faire cela ? :-)
Si vous voulez surcharger une fonction prédéfinie, telle qu'open(), alors vous devrez importer la nouvelle définition depuis un module différent. Voir "Surcharge des Fonctions Prédéfinies" in perlsub. Il y a aussi un exemple dans "Class::Template" in perltoot.
Si vous voulez surcharger un opérateur de Perl, tel que +
ou **
, alors vous voudrez utiliser le pragma use overload
, documentée dans overload.
Si vous parlez d'obscurcir les appels de méthode dans les classes parent, voyez "Polymorphisme" in perltoot.
Quelle est la différence entre appeler une fonction par &foo et foo() ?
Quand vous appelez une fonction par &foo
, vous permettez à cette fonction d'accéder à vos valeurs @_ courantes, et vous court-circuitez les prototypes. Cela signifie que la fonction ne récupère pas un @_ vide, mais le vôtre ! Même si l'on ne peut pas exactement parler d'un bug (c'est documenté de cette façon dans perlsub), il serait difficile de considérer cela comme une caractéristique dans la plupart des cas.
Quand vous appelez votre fonction par &foo()
, alors vous obtenez effectivement une nouvelle @_, mais le prototypage est toujours court-circuité.
Normalement, vous préférerez appeler une fonction en utilisant foo()
. Vous ne pouvez omettre les parenthèses que si la fonction est déjà connue par le compilateur parce qu'il a déjà vu sa définition (use
mais pas require
), ou via une référence en avant ou une déclaration use subs
. Même dans ce cas, vous obtenez une @_ propre sans aucune des vieilles valeurs suintant là où elles ne devraient pas.
Comment créer une instruction switch ou case ?
C'est expliqué plus en profondeur dans perlsyn. En bref, il n'existe pas d'instruction case officielle, à cause de la variété des tests possibles en Perl (comparaison numérique, comparaison de chaînes, comparaison de glob, appariement d'expression rationnelle, comparaisons surchargées, ...). Larry ne pouvait pas se décider sur la meilleure façon de faire cela, il a donc laissé tomber, et c'est resté sur la liste des voeux pieux depuis perl1.
La réponse générale est d'écrire une construction comme celle-ci :
for ($variable_to_test) {
if (/pat1/) { } # faire quelque chose
elsif (/pat2/) { } # faire quelque chose d'autre
elsif (/pat3/) { } # faire quelque chose d'autre
else { } # défaut
}
Voici un exemple simple de switch basé sur un appariement de motif, cette fois mis en page de façon à le faire ressembler un peu plus à une instruction switch. Nous allons créer une conditionnelle multi-voies basée sur le type de référence stockée dans $whatchamacallit :
SWITCH: for (ref $whatchamacallit) {
/^$/ && die "not a reference";
/SCALAR/ && do {
print_scalar($$ref);
last SWITCH;
};
/ARRAY/ && do {
print_array(@$ref);
last SWITCH;
};
/HASH/ && do {
print_hash(%$ref);
last SWITCH;
};
/CODE/ && do {
warn "can't print function ref";
last SWITCH;
};
# DEFAULT
warn "User defined type skipped";
}
Voir perlsyn/"BLOCs de Base et Instruction Switch"
pour beaucoup d'autres exemples dans ce style.
Vous devriez parfois changer les positions de la constante et de la variable. Par exemple, disons que vous voulez tester une réponse parmi de nombreuses vous ayant été données, mais d'une façon insensible à la casse qui permette aussi les abréviations. Vous pouvez utiliser la technique suivante si les chaînes commencent toutes par des caractères différents, ou si vous désirez arranger les correspondances de façon que l'une ait la précédence sur une autre, tout comme "SEND"
a la précédence sur "STOP"
ici :
chomp($answer = <>);
if ("SEND" =~ /^\Q$answer/i) { print "Action is send\n" }
elsif ("STOP" =~ /^\Q$answer/i) { print "Action is stop\n" }
elsif ("ABORT" =~ /^\Q$answer/i) { print "Action is abort\n" }
elsif ("LIST" =~ /^\Q$answer/i) { print "Action is list\n" }
elsif ("EDIT" =~ /^\Q$answer/i) { print "Action is edit\n" }
Une approche totalement différente est de créer une référence de hachage ou de fonction.
my %commands = (
"happy" => \&joy,
"sad", => \&sullen,
"done" => sub { die "See ya!" },
"mad" => \&angry,
);
print "How are you? ";
chomp($string = <STDIN>);
if ($commands{$string}) {
$commands{$string}->();
} else {
print "No such command: $string\n";
}
Comment intercepter les accès aux variables/fonctions/méthodes indéfinies ?
La méthode AUTOLOAD, dont il est question dans "Autochargement" in perlsub et "AUTOLOAD : les méthodes mandataires" in perltoot, vous permet de capturer les appels aux fonctions et aux méthodes indéfinies.
Lorsqu'on en vient aux variables indéfinies qui provoqueraient un avertissement sous -w
, vous pouvez utiliser un handler pour capturer le pseudo-signal __WARN__
comme ceci :
$SIG{__WARN__} = sub {
for ( $_[0] ) { # voici une instruction switch
/Use of uninitialized value/ && do {
# l'avertissement devient fatal
die $_;
};
# les autres cas d'avertissement à intercepter pourraient
# venir ici;
warn $_;
}
};
Pourquoi une méthode incluse dans ce même fichier ne peut-elle pas être trouvée ?
Quelques raisons possibles : votre héritage se trouble, vous avez fait une faute de frappe dans le nom de la méthode, ou l'objet est d'un mauvais type. Regardez dans perltoot pour des détails là-dessus. Vous pourriez aussi utiliser print ref($object)
pour déterminer dans quelle classe $object
a été consacré.
Une autre raison possible de problèmes est que vous avez utilisé la syntaxe d'objet indirect (eg, find Guru "Samy"
) sur le nom d'une classe avant que Perl n'ait vu qu'un tel paquetage existe. Il est plus sage de s'assurer que vos paquetages sont tous définis avant de commencer à les utiliser, ce qui sera fait si vous utilisez l'instruction use
au lieu de require
. Sinon, assurez-vous d'utiliser la notation fléchée à la place (eg, Guru->find("Samy")
). La notation d'objet est expliquée dans perlobj.
Assurez-vous de vous renseigner sur la création des modules en lisant perlmod et les périls des objets indirects dans "AVERTISSEMENT" in perlobj.
Comment déterminer mon paquetage courant ?
Si vous êtes juste un programme pris au hasard, vous pouvez faire ceci pour découvrir quel est le paquetage compilé sur le moment :
my $packname = __PACKAGE__;
Mais si vous êtes une méthode et que vous voulez afficher un message d'erreur qui inclut le genre d'objet sur lequel vous avez été appelée (qui n'est pas nécessairement le même que celui dans lequel vous avez été compilée) :
sub amethod {
my $self = shift;
my $class = ref($self) || $self;
warn "called me from a $class object";
}
Comment commenter un grand bloc de code perl ?
Utilisez le POD embarqué pour le dévalider :
# le programme est ici
=pour personne
Ce paragraphe est en commentaire
# le programme continue
=begin texte de commentaire
tout ce truc
ici sera ignoré
par tout le monde
=end texte de commentaire
=cut
Cela ne peut pas se placer n'importe où. Vous devez mettre une directive pod là où l'analyseur syntaxique attend une nouvelle instruction, et pas juste au milieu d'une expression ou d'une autre production grammaticale arbitraire de yacc.
Comment supprimer un paquetage ?
Utilisez ce code, fourni par Mark-Jason Dominus:
sub scrub_package {
no strict 'refs';
my $pack = shift;
die "Shouldn't delete main package"
if $pack eq "" || $pack eq "main";
my $stash = *{$pack . '::'}{HASH};
my $name;
foreach $name (keys %$stash) {
my $fullname = $pack . '::' . $name;
# Get rid of everything with that name.
undef $$fullname;
undef @$fullname;
undef %$fullname;
undef &$fullname;
undef *$fullname;
}
}
Ou, si vous utilisez une version récente de Perl, vous pouvez juste utiliser à la place la fonction Symbol::delete_package().
Comment utiliser une variable comme nom de variable ?
Les débutants pensent souvent qu'ils veulent qu'une variable contienne le nom d'une variable.
$fred = 23;
$varname = "fred";
++$$varname; # $fred vaut maintenant 24
Ceci marche parfois, mais c'est une très mauvaise idée pour deux raisons.
La première est que cela ne marche que pour les variables globales. Cela signifie ci-dessus que si $fred est une variable lexicale créée avec my(), alors le code ne marchera pas du tout : vous accéderez accidentellement la variable globale et passerez complètement par-dessus le lexical privé. Les variables globales sont mauvaises car elles peuvent facilement entrer en collision accidentellement et produisent en général un code non-extensible et sonfus.
Les références symboliques sont interdites sous le pragma use strict
. Ce ne sont pas de vraies références et par conséquent ne sont pas comptées dans les références ni traitées par le ramasse-miettes.
L'autre raison qui fait qu'utiliser une variable pour contenir le nom d'une autre variable est une mauvaise idée est que la question provient souvent d'un manque de compréhension des structures de données de Perl, en particulier les hachages. En utilisant des références symboliques, vous utilisez juste le hachage de la table de symboles du paquetage (comme %main::
) à la place d'un hachage défini par l'utilisateur. La solution est d'utiliser à la place votre propre hachage ou une vraie référence.
$fred = 23;
$varname = "fred";
$USER_VARS{$varname}++; # ce n'est pas $$varname++
Ici nous utilisons le hachage %USER_VARS à la place de références symboliques. Parfois, cela est nécessaire quand on lit des chaînes auprès de l'utilisateur avec des références de variable et qu'on veut les étendre aux valeurs des variables du programme perl. C'est aussi une mauvaise idée car cela fait entrer en conflit l'espace de noms adressable par le programme et celui adressable par l'utilisateur. Au lieu de lire une chaîne et de la développer pour obtenir le contenu des variables de votre programme :
$str = 'this has a $fred and $barney in it';
$str =~ s/(\$\w+)/$1/eeg; # besoin d'un double eval
Il serait meilleur de conserver un hachage comme %USER_VARS et d'avoir des références de variable pointant effectivement vers des entrées de ce hachage :
$str =~ s/\$(\w+)/$USER_VARS{$1}/g; # pas de /e du tout ici
C'est plus rapide, plus propre, et plus sûr que l'approche précédente. Bien sûr, vous n'avez pas besoin d'utiliser un dollar. Vous pourriez utiliser votre propre caractère pour rendre le tout moins confus, comme des symboles de pourcents, etc.
$str = 'this has a %fred% and %barney% in it';
$str =~ s/%(\w+)%/$USER_VARS{$1}/g; # pas de /e du tout ici
Une autre raison qui fait que les gens pensent parfois vouloir qu'une variable contienne le nom d'une autre variable est qu'ils ne savent pas comment construire des structures de données correctes en utilisant des hachages. Par exemple, supposons qu'ils aient voulu deux hachages dans leur programme : %fred et %barney, et utiliser une autre variable scalaire pour s'y référer par leurs noms.
$name = "fred";
$$name{WIFE} = "wilma"; # définit %fred
$name = "barney";
$$name{WIFE} = "betty"; # définit %barney
C'est toujours une référence symbolique, avec toujours les problèmes énumérés ci-dessus sur les bras. Il serait bien meilleur d'écrire :
$folks{"fred"}{WIFE} = "wilma";
$folks{"barney"}{WIFE} = "betty";
Et de simplement utiliser un hachage multiniveau pour commencer.
La seule occasion où vous devez absolument utiliser des références symboliques est lorsque vous devez réellement vous référer à la table de symboles. Cela peut se produire parce qu'il s'agit de quelque chose dont on ne peut pas prendre une vraie référence, tel qu'un nom de format. Faire cela peut aussi être important pour les appels de méthodes, puisqu'ils passent toujours par la table de symboles pour leur résolution.
Dans ces cas, vous devrez supprimer temporairement les strict 'refs'
de façon à pouvoir jouer avec la table de symboles. Par exemple :
@colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
no strict 'refs'; # renege for the block
*$name = sub { "<FONT COLOR='$name'>@_</FONT>" };
}
Toutes ces fonctions (red(), blue(), green(), etc.) apparaissent comme différentes, mais le vrai code dans la fermeture n'a effectivement été compilé qu'une fois.
Vous voudrez donc parfois utiliser les références symboliques pour manipuler directement la table de symboles. Cela n'a pas d'importance pour les formats, les handles, et les sous-programmes, car ils sont toujours globaux -- vous ne pouvez pas utiliser my() sur eux. Mais pour les scalaires, les tableaux et les hachages -- et habituellement pour les sous-programmes -- vous préférerez probablement n'utiliser que les références dures.
AUTEUR ET COPYRIGHT
Copyright (c) 1997-1999 Tom Christiansen et Nathan Torkington. Tous droits réservés.
Lorsqu'inclus comme partie de la Version Standard de Perl, ou comme partie de sa documentation complète qu'il soit imprimé ou autrement diffusé, ce travail ne peut être distribué que sous les termes de la Licence Artistique de Perl. Par conséquent, toute distribution de ce fichier ou de ses dérivés à l'extérieur de ce paquetage nécessite qu'un arrangement particulier soit fait avec le titulaire du copyright.
Contrairement à sa distribution, tous les exemples de code de ce fichier sont placés par le présent acte dans le domaine public. Vous avez l'autorisation et êtes encouragés à utiliser ce code dans vos propres programmes pour vous amuser ou pour gagner de l'argent selon votre humeur. Un simple commentaire dans le code créditant les auteurs serait courtois mais n'est pas requis.
TRADUCTION
Version
Cette traduction française correspond à la version anglaise distribuée avec perl 5.6.0. Pour en savoir plus concernant ces traductions, consultez http://perl.enstimac.fr/.
Traducteur
Roland Trique <roland.trique@free.fr>.
Relecture
Régis Julié <Regis.Julie@cetelem.fr>, Gérard Delafond.