perlref - Références et structures de données imbriquées en Perl
Ce deocument est la documentation complète abordant tous les aspects des références. Pour une introduction n'abordant que les fonctionnalités essentielles et donc plus courte et surtout plus pédagogique, voir la page de manuel perlreftut.
Avant la version 5 de Perl, il était difficile de représenter des structures de données complexes car toutes les références devaient être symboliques (et même dans ce cas, il était difficile de référencer une variable à la place d'une entrée symbolique de tableau). Desormais, non seulement Perl facilite l'utilisation de références symboliques à des variables, mais il vous laisse en plus la possibilité d'avoir des références ``dures'' à tout morceau de données ou de code. N'importe quel scalaire peut contenir une référence dure. Comme les tableaux et les tables de hachage contiennent des scalaires, vous pouvez désormais construire facilement des tableaux de tableaux, des tableaux de tables de hachage, des tables de hachage de tableau, des tableaux de tables de hachage de fonctions, etc.
Les références dures sont intelligentes : elles conservent la trace du nombre de références pour vous, libérant automatiquement la structure référencée quand son compteur de références atteint zéro. (Le compteur de références pour des valeurs dans des structures de données auto-référencées ou cycliques ne pourra pas atteindre zéro sans un petit coup de pouce. Cf. « Ramasse-miettes à deux phases » dans la page de manuel perlobj pour une explication détaillée.) Si cette structure s'avère être un objet, celui-ci est détruit. Cf. la page de manuel perlobj pour de plus amples renseignements sur les objets. (Dans un certain sens, tout est objet en Perl, mais d'habitude nous réservons ce mot pour les références à des structures qui ont été officiellement ``bénies'' dans un paquetage de classes.
Les références symboliques sont des noms de variables ou d'autres
objets, tout comme un lien symbolique dans un système de fichiers Unix
ne contient a peu de choses près que le nom d'un fichier. La notation
*glob
est un type de référence symbolique. (Les références
symboliques sont parfois appelées ``références douces'' mais évitez de
les appeler ainsi ; les références sont déjà suffisamment confuses
sans ces synonymes inutiles.)
Au contraire, les références dures ressemblent plus aux liens durs dans un système de fichiers Unix : elles sont utilisées pour accéder à un objet sous-jacent sans se préoccuper de son (autre) nom. Quand le mot ``référence'' est utilisé sans adjectif, comme dans le paragraphe suivant, il est habituellement question d'une référence dure.
Les références sont faciles à utiliser en Perl. Il n'existe qu'un principe majeur : Perl ne référence et ne déréférence jamais de façon implicite. Quand un scalaire contient une référence, il se comporte toujours comme un simple scalaire. Il ne devient pas magiquement un tableau, une table de hachage ou une routine. Vous devez le lui préciser explicitement, en le déréférençant.
Les références peuvent être créées de plusieurs façons.
$scalarref = \$foo; $arrayref = \@ARGV; $hashref = \%ENV; $coderef = \&handler; $globref = \*foo;
Il est impossible de créer une véritable référence à un descripteur
d'E/S (descripteur de fichier ou de répertoire) en utilisant
l'opérateur backslash. Le mieux que vous puissiez obtenir est une
référence à un typeglob, qui est en fait une entrée complète de la
table des symboles. Voir l'explication de la syntaxe *foo{THING}
ci-dessous. Quoi qu'il en soit, vous pouvez toujours utiliser les
typeglobs et les globrefs comme s'il étaient des descripteur d'E/S.
$arrayref = [1, 2, ['a', 'b', 'c']];
Ici, nous avons créé une référence à un tableau anonyme de trois
éléments, dont le dernier est lui-même une référence à un autre
tableau anonyme de trois éléments. (La syntaxe multidimensionnelle
décrite plus loin peut être utilisée pour y accéder. Par exemple,
après le code ci-dessus, $arrayref->[2][1]
aura la
valeur ``b''.)
Prendre une référence à une liste enumérée n'est pas la même chose que d'utiliser des crochets (c'est plutôt la même chose que créer une liste de références !)
@list = (\$a, \@b, \%c); @list = \($a, @b, %c); # identique !
À l'exception de \(@foo)
qui retourne une liste de références au
contenu de @foo
, et non pas une référence à @foo
lui-même. Il
en est de même pour %foo
sauf évidemment pour les clés elle-mêmes
qui seront simplement recopiées (puisque les clés sont justes des
chaînes de caractères et non des scalaires au sens large).
$hashref = { 'Adam' => 'Eve', 'Clyde' => 'Bonnie', };
Les composants de table de hachage et de tableau comme ceux-ci peuvent
être librement mélangés pour produire une structure aussi complexe que
vous le souhaitez. La syntaxe multidimensionnelle décrite ci-dessous
fonctionne pour ces deux cas. Les valeurs ci-dessus sont littérales
mais des variables et expressions fonctionneraient de la même manière,
car l'opérateur d'affectation en Perl (même à l'intérieur d'un
local()
ou d'un my())
sont des
instructions exécutables et non pas des déclarations à la compilation.
Comme les accolades sont utilisées pour bien d'autres choses, y
compris les BLOCs, vous pourriez être amené à devoir expliciter les
accolades au début d'une instruction en ajoutant un +
ou un
return
devant, de telle sorte que Perl comprenne que l'accolade
ouvrante n'est pas le commencement d'un BLOC. Les économies réalisées
et la valeur mnémotechnique des accolades valent bien cet
embarrassement supplémentaire.
Par exemple, si vous désirez une fonction qui crée une nouvelle table de hachage et retourne une référence à celle-ci, vous avez ces possibilités :
sub hashem { { @_ } } # silencieusement faux sub hashem { +{ @_ } } # correct sub hashem { return { @_ } } # correct
D'un autre côté, si vous souhaitez l'autre signification, vous pouvez faire ceci :
sub showem { { @_ } } # ambigu (correct pour le moment # mais pourrait changer) sub showem { {; @_ } } # correct sub showem { { return @_ } } # correct
Les +{
et {;
en début servent à différencier de manière
explicite soit une référence à un TABLE DE HACHAGE soit un BLOC.
sub
sans nom de routine :
$coderef = sub { print "Boink!\n" };
Notez la présence du point-virgule. À part le fait que le code à
l'intérieur n'est pas exécuté immédiatement, un sub {}
n'est ni
plus ni moins qu'une déclaration comme opérateur, tout comme do{}
ou eval{}
. (Peu importe le nombre de fois que vous allez exécuter
cette ligne particulière -- à moins que vous soyez dans un
eval("...")
-- $coderef fera toujours référence à la MÊME
routine anonyme.)
Les routines anonymes fonctionnent comme les fermetures, en respectant les variables my(), c'est-à-dire les variables lexicalement visibles dans la portée actuelle. La fermeture est une notion provenant de l'univers Lisp qui indique que, si vous définissez une fonction anonyme dans un contexte lexical particulier, elle essaiera de fonctionner dans ce contexte, même quand elle est appelée en-dehors de ce contexte.
En termes plus humains, c'est une façon amusante de passer des arguments à une routine, aussi bien lorsque vous la définissez que lorsque vous l'appelez. C'est très utile pour mettre au point des petits morceaux de code à exécuter plus tard, comme les callbacks. Vous pouvez même faire de l'orienté objet avec ça bien que Perl fournisse déjà un autre mécanisme pour le faire (voir la page de manuel perlobj).
Vous pouvez aussi considérer la fermeture comme une façon d'écrire un modèle de routine sans utiliser eval(). Voici un petit exemple de fonctionnement des fermetures :
sub newprint { my $x = shift; return sub { my $y = shift; print "$x, $y !\n"; }; } $h = newprint("Bonjour"); $g = newprint("Salutations");
# Un ange passe...
&$h("monde"); &$g("humains");
Ce qui affiche
Bonjour, monde ! Salutations, humains !
Notez en particulier que $x continue à référencer la valeur passée à
newprint()
bien que le ``my $x'' semble être hors de la portée au
moment où la routine anonyme est exécutée. Voici donc ce qu'est la
fermeture.
À propos, ceci ne s'applique qu'aux variables lexicales. Les variables dynamiques continuent de fonctionner comme elle l'ont toujours fait. La fermeture n'est pas une chose dont la plupart des programmeurs Perl ont besoin de s'embarraser pour commencer.
new()
et
appelés indirectement :
$objref = new Doggie (Tail => 'short', Ears => 'long');
Mais il n'est pas nécessaire d'avoir :
$objref = Doggie->new(Tail => 'short', Ears => 'long');
use Term::Cap; $terminal = Term::Cap->Tgetent( { OSPEED => 9600 });
use Tk; $main = MainWindow->new(); $menubar = $main->Frame(-relief => "raised", -borderwidth => 2)
$scalarref = *foo{SCALAR}; $arrayref = *ARGV{ARRAY}; $hashref = *ENV{HASH}; $coderef = *handler{CODE}; $ioref = *STDIN{IO}; $globref = *foo{GLOB}; $formatref = *foo{FORMAT};
Tout ceci s'explique de lui-même, à part *foo{IO}
. Il retourne le
descripteur d'E/S utilisé pour les descripteurs de fichiers
(open dans la page de manuel perlfunc), de sockets (socket dans la page de manuel perlfunc et
socketpair dans la page de manuel perlfunc) et de répertoires (opendir dans la page de manuel perlfunc). Pour
des raisons de compatibilités avec les versions précédentes de Perl,
*foo{FILEHANDLE} est un synonyme de *foo{IO}. Si les avertissements
sont actifs, l'utilisation de ce synonyme affichera un message.
*foo{TURC}
retourne un indéfini si ce TRUC particulier n'a pas été
utilisé auparavant, sauf dans le cas des scalaires. *foo{SCALAR}
retourne une référence à un scalaire anonyme si $foo n'a pas encore
été utilisé. Ceci pourrait changer dans une prochaine version.
*foo{IO}
est une autre manière d'accéder au mécanisme \*HANDLE
indiqué dans « Typeglobs et Handles de Fichiers » dans la page de manuel perldata pour
passer des descripteurs de fichiers comme arguments ou comme valeur de
retour de routines, ou pour les stocker dans des structures de données
plus grandes. L'inconvénient, c'est qu'il ne crée pas de nouveau
descripteur de fichier pour vous. L'avantage, c'est qu'il y a moins de
risque d'aller au-delà de ce que vous souhaitez qu'avec une affection
de typeglob (il passe tout de même les descripteurs de fichier et de
répertoire). Ceci étant, si vous l'affectez à un scalaire au lieu d'un
typeglob comme dans l'exemple ci-dessous, vous êtes couvert dans tous
les cas.
splutter(*STDOUT); # passe tout le glob splutter(*STDOUT{IO}); # ne passe que le descripteur # de fichier et de répertoire
sub splutter { my $fh = shift; print $fh "her um well a hmmm\n"; }
$rec = get_rec(*STDIN); # passe tout le glob $rec = get_rec(*STDIN{IO}); # ne passe que le descripteur # de fichier et de répertoire
sub get_rec { my $fh = shift; return scalar <$fh>; }
C'est tout pour la création de références. Maintenant, vous devez sûrement mourir d'envie de savoir comment utiliser ces références pour en revenir à vos données perdues depuis longtemps. Il existe plusieurs méthodes de base.
$bar = $$scalarref; push(@$arrayref, $filename); $$arrayref[0] = "January"; $$hashref{"KEY"} = "VALUE"; &$coderef(1,2,3); print $globref "output\n";
Il est important de comprendre qu'ici, nous ne déréférençons pas en
particulier $arrayref[0]
ou $hashref{"KEY"}
. Le déréférencement
de la variable scalaire a lieu avant toute recherche de clé. Tout
ce qui est plus complexe qu'une simple variable scalaire doit utiliser
les méthodes 2 et 3 ci-dessous. un ``simple scalaire'' inclut
toutefois un identifiant qui utilise lui-même la méthode 1 de façon
récursive. Le code suivant imprime par conséquent ``howdy''.
$refrefref = \\\"howdy"; print $$$$refrefref;
$bar = ${$scalarref}; push(@{$arrayref}, $filename); ${$arrayref}[0] = "January"; ${$hashref}{"KEY"} = "VALUE"; &{$coderef}(1,2,3); $globref->print("output\n"); # ssi IO::Handle est chargé
Il est vrai que c'est un peu idiot d'utiliser des accolades dans ce cas-là, mais le BLOC peut contenir n'importe quelle expression, en particulier une expression subscript telle que celle-ci :
&{ $dispatch{$index} }(1,2,3); # appel la routine correcte
Comme il est possible d'omettre les accolades dans le cas simple de
$$x
, les gens font souvent l'erreur de considérer le
déréférencement comme des opérateurs propres et se posent des questions à
propos de leur précédence. Mais s'ils en étaient, vous pourriez
utiliser des parenthèses à la place des accolades. Ce qui n'est pas le
cas. Remarquez la différence ci-dessous. Le cas 0 est un
raccourci du cas 1 mais pas du cas 2.
$$hashref{"KEY"} = "VALUE"; # CAS 0 ${$hashref}{"KEY"} = "VALUE"; # CAS 1 ${$hashref{"KEY"}} = "VALUE"; # CAS 2 ${$hashref->{"KEY"}} = "VALUE"; # CAS 3
Le cas 2 est aussi décevant dans le sens que vous accédez à une variable appelée %hashref, sans déréférencer par $hashref la table de hachage qu'il référence probablement. Ceci correspond au cas 3.
$arrayref->[0] = "January"; # Élément de tableau $hashref->{"KEY"} = "VALUE"; # Élément de table de hachage $coderef->(1,2,3); # Appel d'une routine
La partie gauche de la flèche peut être n'importe quelle expression
retournant une référence, y compris un déréférencement
précédent. Notez que $array[$x]
n'est pas ici la même chose que
$array->[$x]
:
$array[$x]->{"foo"}->[0] = "January";
C'est un des cas que nous avons mentionnés plus tôt et dans lequel les
références peuvent venir à exister dans un contexte lvalue. Avant
cette instruction, $array[$x]
peut avoir été indéfini. Dans ce cas,
il est automatiquement défini avec une référence de table de hachage,
de telle sorte que nous puissions rechercher {"foo"}
dedans. De la
même manière, $array[$x]->{"foo"}
sera automatiquement défini
avec une référence de tableau, de telle façon que nous puissions
rechercher dedans. Ce processus est appelé autovivification.
Encore une petite chose ici : la flèche est facultative entre les crochets subscripts. Vous pouvez donc abbréger le code ci-dessus en :
$array[$x]{"foo"}[0] = "January";
Ce qui, dans le cas dégénéré de la seule utilisation de tableaux ordinaires, vous donne des tableaux multidimensionnels tout comme en C :
$score[$x][$y][$z] += 42;
Bon, d'accord, pas complètement comme en C, en fait. Le C ne sait pas comment aggrandir ses tableaux à la demande. Perl le sait.
L'utilisation d'un nombre ou d'une chaîne en tant que référence en fait une référence symbolique comme expliqué plus haut. L'utilisation d'une référence en tant que nombre la transforme en un entier représentant son emplacement en mémoire. Le seul usage intéressant est la comparaison numérique de deux références pour savoir si elles se réfèrent au même emplacement.
if ($ref1 == $ref2) { print "refs 1 et 2 font référence à la même chose\n"; }
L'utilisation d'une référence en tant que chaîne produit à la fois le
type d'objet qu'elle référence en incluant le nom du paquetage l'ayant
éventuellement consacré (par bless())
comme expliqué dans la page de manuel perlobj,
et aussi son adresse mémoire numérique en hexadécimal. L'opérateur
ref()
produit juste le type d'objet lié à la référence, sans
l'adresse. Voir ref dans la page de manuel perlfunc pour plus de détails et des exemples
d'utilisation.
L'opérateur bless()
peut être utilisé pour associer l'objet, sur
lequel pointe une référence, avec un paquetage fonctionnant comme une
classe d'objets. Cf. la page de manuel perlobj.
Un typeglob peut être déréférencé de la même façon qu'une référence,
car la syntaxe de déréférencement indique toujours le type de
référence souhaité. Par conséquent, ${*foo}
et ${\$foo}
indique
tous les deux la même variable scalaire.
Voici un truc pour interpoler l'appel d'une routine dans une chaine de caractères :
print "My sub returned @{[mysub(1,2,3)]} that time.\n";
La façon dont ça marche, c'est que lorsque le @{...}
est aperçu à
l'intérieur des guillemets de la chaîne de caractères, il est évalué
comme un bloc. Le bloc crée une référence à une tableau anonyme
contenant le résultat de l'appel à mysub(1,2,3)
. Le bloc entier
retourne ainsi une référence à un tableau, qui est alors déréférencé
par @{...}
et inséré dans la chaîne de caractères entre
guillemets. Cette chipotterie est aussi utile pour des expressions
arbitraires :
print "That yields @{[$n + 5]} widgets\n";
Nous avons déjà expliqué que, quand c'est nécessaire, les références devenaient existantes si elles sont définies, mais nous n'avons pas dit ce qui arrivait lorsqu'une valeur utilisée comme référence est déjà définie mais n'est pas une référence dure. Si vous l'utilisez comme référence dans ce cas-là, elle sera traitée comme une référence symbolique. C'est-à-dire que la valeur du scalaire est considérée comme le nom d'une variable, plutôt que comme un lien direct vers une (éventuelle) valeur anonyme.
En général, les gens s'attendent à ce que ça fonctionne de cette façon. C'est donc comme ça que ça marche.
$name = "foo"; $$name = 1; # Affecte $foo ${$name} = 2; # Affecte $foo ${$name x 2} = 3; # Affecte $foofoo $name->[0] = 4; # Affecte $foo[0] @$name = (); # Efface @foo &$name(); # Appelle &foo() (comme en Perl 4) $pack = "THAT"; ${"${pack}::$name"} = 5; # Affecte $THAT::foo sans évaluation
C'est très puissant, et potentiellement dangereux, dans le sens où il est possible de vouloir (avec la plus grande sincérité) utiliser une référence dure, et utiliser accidentellement une référence symbolique à la place. Pour vous en prémunir, vous pouvez utiliser
use strict 'refs';
et seules les références dures seront alors autorisées dans le reste du bloc l'incluant. Un bloc imbriqué peut inverser son effet avec
no strict 'refs';
Seuls les variables (globales, même si elles sont localisées) de
paquetage sont visibles par des références symboliques. Les variables
lexicales (déclarées avec my())
ne font pas partie de la table des
symboles, et sont donc invisibles à ce mécanisme. Par exemple :
local $value = 10; $ref = "value"; { my $value = 20; print $$ref; }
Ceci imprimera 10 et non pas 20. Souvenez-vous que local()
affecte les
variables de paquetage, qui sont toutes ``globales'' au paquetage.
Une nouvelle fonctionnalité contribuant à la lisibilité en perl version 5.001 est que les crochets autour d'une référence symbolique se comportent comme des apostrophes, tout comme elles l'ont toujours été dans une chaîne de caractères. C'est-à-dire que
$push = "pop on "; print "${push}over";
a toujours imprimé ``pop on over'', même si push est un mot réservé. Ceci a été généralisé pour fonctionner de même en dehors de guillemets, de telle sorte que
print ${push} . "over";
et même
print ${ push } . "over";
auront un effet identique. (Ceci aurait provoqué une erreur syntaxique en Perl 5.000, bien que Perl 4 l'autorisait dans une forme sans espaces.) Cette construction n'est pas considérée comme une référence symbolique lorsque vous utilisez strict refs :
use strict 'refs'; ${ bareword }; # Correct, signifie $bareword. ${ "bareword" }; # Erreur, référence symbolique.
De façon similaire, à cause de tout le subscripting qui est effectué en utilisant des mots simples, nous avons appliqué la même règle à tout mot simple qui soit utilisé pour le subscripting d'une table de hachage. Désormais, au lieu d'écrire
$array{ "aaa" }{ "bbb" }{ "ccc" }
vous pourrez donc juste écrire
$array{ aaa }{ bbb }{ ccc }
sans vous inquiéter du fait que les subscripts soient ou non des mots réservés. Dans les rares cas où vous souhaiteriez faire quelque chose comme :
$array{ shift }
vous pouvez en forcer l'interprétation comme un mot réservé en ajoutant n'importe quoi qui soit plus qu'un mot simple :
$array{ shift() } $array{ +shift } $array{ shift @_ }
La directive use warnings
ou l'option -w vous avertira si un mot
réservé est interprété comme une chaîne de caractères. Mais il ne vous
avertira plus si vous utilisez des mots en minuscules, car la chaîne
de caractères est entre guillemets de façon effective.
AVERTISSEMENT : cette section traite de fonctionnalités expérimentales. Certains détails pourraient changer sans annonce particulière dans les prochaines versions.
NOTE : la partie visible de l'implémentation actuelle des pseudo-tables de hachage (l'utilisation singulière du premier élément du tableau) est dépréciée à partir de Perl 5.8.0 et disparaîtra dans Perl 5.10.0. Cette fonctionnalité sera implémentée autrement. Au-delà de l'interface actuelle qui est particulièrement horrible, l'implémentation actuelle ralentit notablement l'utilisation normale des tableaux et des tables de hachage. La directive 'fields' restera disponible.
Avec la version 5.005 de Perl, vous pouvez désormais utiliser une référence à un tableau dans un contexte qui exigerait normalement une référence à une table de hachage. Ceci vous permet d'accéder aux éléments d'un tableau en utilisant des noms symboliques, comme s'ils étaient les champs d'une structure.
Pour que cela fonctionne, le tableau doit contenir des informations supplémentaires. Le premier élément du tableau doit être une référence à une table de hachage qui associe les noms de champs avec les indices du tableau. Voici un exemple :
$struct = [{foo => 1, bar => 2}, "FOO", "BAR"];
$struct->{foo}; # identique à $struct->[1], c'est-à-dire "FOO" $struct->{bar}; # identique à $struct->[2], c'est-à-dire "BAR"
keys %$struct; # retournera ("foo", "bar") dans un certain ordre values %$struct; # retournera ("FOO", "BAR") dans le meme certain ordre
while (my($k,$v) = each %$struct) { print "$k => $v\n"; }
Perl déclenchera une exception si vous essayez d'accéder à des champs
inexistants. Pour éviter les incohérences, utilisez toujours la
fonction fields::phash() fournie par la directive fields
.
use fields; $pseudohash = fields::phash(foo => "FOO", bar => "BAR");
Pour de meilleures performances, Perl peut aussi effectuer la traduction des noms de champs en indices de tableau lors de la compilation pour les références à des objets typées. Voir fields.
Il existe deux moyens de vérifier l'existence d'une clé dans une pseudo-table de hachage. Le premier est d'utiliser exist(). Cela teste si ce champ donné a déjà été utilisé. Ce comportement est le même que celui d'une table de hachage normale. Par exemple :
use fields; $phash = fields::phash([qw(foo bar pants)], ['FOO']); $phash->{pants} = undef;
print exists $phash->{foo}; # vrai, 'foo' est valué dans la declaration print exists $phash->{bar}; # faux, 'bar' n'a jamais été utilisé print exists $phash->{pants}; # vrai, 'pants' a été utilisé
Le second moyen est d'utiliser exists()
sur la table de hachage
présente comme premier élément du tableau. Cela teste si ce champ est
un champ valide pour cette pseudo-table de hachage.
print exists $phash->[0]{bar}; # vrai, 'bar' est valide print exists $phash->[0]{shoes}; # faux, 'shoes' ne peut être utilisé
Un appel à delete()
sur un élément d'une pseudo-table de hachage
n'efface que le valeur correspondant à cette clé et non la clé
elle-même. Pour effacer la clé, vous devez l'effacer explicitement de
la table située au premier élément du tableau.
print delete $phash->{foo}; # affiche $phash->[1], "FOO" print exists $phash->{foo}; # faux print exists $phash->[0]{foo}; # vrai, la clé existe encore print delete $phash->[0]{foo}; # maintenant la clé est effacée print $phash->{foo}; # exception déclenchée
Comme expliqué ci-dessus, une fermeture est une fonction anonyme qui a accès aux variables lexicales qui étaient visibles lors de sa compilation. Elle conserve l'accès à ses variables même si elle n'est exécutée que plus tard, comme dans le cas des signaux ou des callbacks en Tk.
Utiliser une fermeture comme modèle de fonction nous permet de générer de nombreuses fonctions agissant de façon similaire. Supposons que vous souhaitiez des fonctions nommées d'après la couleur qu'elles produiront en HTML via la balise FONT :
print "Be ", red("careful"), "with that ", green("light");
Les fonctions red()
et green()
seront très similaires. Pour les créer,
nous allons assigner une fermeture à un typeglob du nom de la fonction
que nous voulonsq construire.
@colors = qw(red blue green yellow orange purple violet); for my $name (@colors) { no strict 'refs'; # Autorise la manipulation de la table de symboles *$name = *{uc $name} = sub { "<FONT COLOR='$name'>@_</FONT>" }; }
Désormais, toutes ces fonctions existent de façon indépendante. Vous
pouvez appeler red(), RED(), blue(), BLUE(), green(), etc. Cette
technique optimise le temps de compilation et l'utilisation de la
mémoire, et elle est aussi moins sujette aux erreurs puisque la
vérification syntaxique a lieu à la compilation. Il est nécessaire
qu'aucune variable de la routine anonyme ne soit lexicale pour créer
une fermeture propre. C'est la raison pour laquelle nous avons un
my
dans notre boucle.
C'est l'un des seuls endroits où donner un prototype à une fermeture a un réel sens. Si vous souhaitiez imposer un contexte scalaire aux arguments de ces fonctions (ce qui n'est probablement pas une bonne idée pour cet exemple particulier), vous auriez pu écrire à la place :
*$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" };
Quoi qu'il en soit, comme la vérification des prototypes a lieu à la compilation, l'affectation ci-dessus est effectuée trop tard pour être vraiment utile. Vous pourriez gérer ça en insérant la boucle entière d'affectations dans un bloc BEGIN, forçant ainsi son exécution pendant la compilation.
L'accès aux lexicaux qui change au-delà des types (comme ceux de la
boucle for
ci-dessus) ne fonctionne qu'avec des fermetures et pas
avec des routines générales. Par conséquent, dans le cas général, les
routines nommées ne s'imbriquent pas proprement, au contraire des
routines anonymes. C'est comme cela parce que les routines nommées
sont crées (et récupèrent les lexicaux externes) une seule fois lors
de la compilation alors que les routines anonymes réalisent cette
récupération à chaque exécution de l'opérateur 'sub'. Si vous êtes
habitué à l'utilisation de routines imbriquées dans d'autres langages
de programmation, avec leurs propres variables privées, il va vous
falloir travailler là-dessus en Perl un tant soit peu. La
programmation intuitive de ce genre de choses implique des
avertissements mystérieux du genre ``will not stay shared''. Par
exemple, ceci ne fonctionnera pas :
sub outer { my $x = $_[0] + 35; sub inner { return $x * 19 } # FAUX return $x + inner(); }
Une solution pourraît être celle-ci :
sub outer { my $x = $_[0] + 35; local *inner = sub { return $x * 19 }; return $x + inner(); }
Maintenant, inner()
ne peut être appelée que de l'intérieur de
outer(), grâce aux affectations temporaires de la fermeture (routine
anonyme). Mais lorsque cela a lieu, elle a un accès normal à la
variable lexicale $x dans la portée de outer().
Ceci a pour effet intéressant de créer une fonction locale à une autre, ce qui n'est pas normalement supporté par Perl.
Vous ne devriez pas (utilement) utiliser une référence comme clé d'une table de hachage. Elle sera convertie en chaîne de caractères :
$x{ \$a } = $a;
Si vous essayez de déréférencer la clé, il n'y aura pas de déréférencement dur et vous ne ferez pas ce que vous souhaitiez. Vous devriez plutôt faire ainsi :
$r = \@a; $x{ $r } = $r;
Et alors, au moins, vous pourrez utiliser les valeurs, par values(), qui seront de véritables références, au contraire des clés, par keys().
Le module standard Tie::RefHash fournit une base de travail pratique pour faire ce genre de choses.
À côté de la documentation standard, du code source peut être instructif. Quelques exemples pathologiques de l'utilisation de références peuvent être trouvées dans le test de régression t/op/ref.t du répertoire source de Perl.
Voir aussi la page de manuel perldsc et la page de manuel perllol, pour l'utilisation de références dans la création de structures de données complexes, et la page de manuel perltoot, la page de manuel perlobj et la page de manuel perlbot pour leur utilisation dans la création d'objets.
Larry Wall
Cette traduction française correspond à la version anglaise distribuée avec perl 5.8.8. Pour en savoir plus concernant ces traductions, consultez http://perl.enstimac.fr/.
Traduction initiale : Jean-Pascal Peltier <jp_peltier@altavista.net>. Mise à jour : Paul Gaborit <paul.gaborit at enstimac.fr>.
Personne pour l'instant.