Perl moderne : 2014

Prenez le contrôle de votre programmation avec le Perl moderne.


précédentsommairesuivant

IV. Le langage Perl

Tout comme une langue parlée, l'ensemble de Perl est une combinaison de plusieurs parties plus petites, mais interdépendantes. Contrairement à la langue parlée, où les nuances, le ton de la voix et l'intuition permettent aux gens de communiquer malgré de légers malentendus et des concepts flous, les ordinateurs et le code source exigent précision. Vous pouvez écrire du code Perl efficace sans connaître tous les détails de chaque fonctionnalité du langage, mais vous devez comprendre comment elles interagissent pour écrire correctement du code Perl.

IV-A. Noms

On trouve des noms (ou identifiants) partout dans les programmes Perl : vous apprenez à les choisir pour des variables, fonctions, paquetages, classes et même descripteurs de fichiers. En Perl, les noms valides commencent tous par une lettre ou un caractère de soulignement et peuvent éventuellement inclure toute combinaison de lettres, chiffres et caractères de soulignement. Lorsque le pragma utf8 (4) (Unicode et chaînes de caractèresUnicode et chaînes de caractères) est activé, vous pouvez utiliser des caractères UTF-8 dans les identifiants. Les identifiants Perl suivants sont tous valides :

 
Sélectionnez
my $nom;
my @_noms_particuliers;
my %Noms_en_Adresses;
sub unNomEmbarassant3;

# avec use utf8; permis
package Ingy::Döt::Net;

Les identifiants suivants sont invalides en Perl :

 
Sélectionnez
my $nom invalide;
my @3;
my %~drapeaux;

package un-nom-dans-le-style-lisp;

Les noms existent principalement au profit du programmeur. Ces règles s'appliquent uniquement aux noms littéraux qui apparaissent dans votre code source, comme sub chercher_tarte ou my $gaufrier. Seulement l'analyseur Perl applique les règles concernant les noms des identifiants. Perl vous permet de désigner des entités par des noms générés lors de l'exécution ou fournis en entrée à un programme. Ces recherches symboliques offrent de la flexibilité au détriment de la sécurité.

En particulier, en invoquant indirectement fonctions ou méthodes ou en recherchant des symboles dans un espace de noms vous permet de contourner l'analyseur Perl. Les recherches symboliques peuvent produire du code source déroutant. Comme Mark Jason Dominus le recommande brillamment (http://perl.plover.com/varvarname.html), privilégiez une table de hachage (Tables de hachageTables de hachage) ou une structure de données imbriquées (Structures de données imbriquéesStructures de données imbriquées) pour les variables nommées, par exemple, $recette1, $recette2 et ainsi de suite.

IV-A-1. Noms de variables et sigils

Les noms de variables commencent toujours par un sigil (un symbole) qui indique le type de valeur de la variable. Les variables scalaires (ScalairesScalaires) utilisent le symbole dollar ($). Les variables tableau (TableauxTableaux) utilisent l'arobase (@). Les variables de hachage (Tables de hachageTables de hachage) utilisent le symbole de pourcentage (%) :

 
Sélectionnez
my $scalaire;
my @tableau;
my %table_de_hachage;

Les sigils vous permettent de séparer les variables en espaces de noms différents. Il est possible — bien que déroutant — de déclarer plusieurs variables du même nom, mais de types différents :

 
Sélectionnez
my ($mauvais_renom, @mauvais_renom, %mauvais_renom);

Cela n'embrouillera pas Perl, mais ce sera le cas des gens qui liront ce code.

Le sigil d'une variable peut changer en fonction de ce que vous faites avec lui (sigils variants). Tout comme le contexte détermine combien d'éléments vous attendez d'une opération ou le type de données auquel vous vous attendez, le sigil régit la façon dont vous manipulez les données d'une variable. Par exemple, vous devez utiliser le sigil scalaire ($) pour accéder à un seul élément d'un tableau ou d'une table de hachage :

 
Sélectionnez
my $hash_element  = $hash{ $clef };
my $array_element = $array[ $indice ]

$hash{ $clef }     = 'valeur';
$array[ $indice ]  = 'item';

Le parallèle avec le contexte de quantité est important. L'utilisation d'un élément scalaire d'une collection en tant que lvalue (la cible d'une affectation du côté gauche du signe =) impose un contexte scalaire (ContexteContexte) sur la rvalue (la valeur assignée, à droite du signe =).

De même, l'accès à plusieurs éléments d'une table de hachage ou d'un tableau — on appelle cela prélever une tranche du tableau ou du hachage — utilise le symbole arobase (@) et impose un contexte de liste, même si la liste elle-même a zéro ou un élément :

 
Sélectionnez
my @hash_elements  = @hash{ @clefs };
my @array_elements = @array[ @indices ];

my %hash;
@hash{ @clefs }    = @valeurs;

Le moyen le plus fiable pour déterminer le type d'une variable — scalaire, tableau ou hachage — consiste à examiner les opérations effectuées sur elle. Les scalaires prennent en charge toutes les opérations de base, telles que les manipulations numériques, booléennes et de chaînes de caractères. Les tableaux permettent l'accès indexé par l'intermédiaire des crochets. Les tables de hachage permettent l'accès par clé via des accolades.

IV-A-2. Espaces de noms

Perl fournit un mécanisme permettant de grouper des fonctions et des variables similaires dans leurs propres espaces de noms — namespaces (PaquetagesPaquetages). Un espace de noms est une collection de symboles groupés sous un nom unique. Perl permet des espaces de noms à plusieurs niveaux, avec des noms reliés par des deux-points doubles (::), où Glacerie::CornetDeGlace fait référence à un ensemble logique de variables et fonctions connexes, comme ajouter_boule() et napper_de_sauce_caramel().

Au sein d'un espace de noms, vous pouvez utiliser le nom court de ses membres. En dehors de l'espace de noms, vous devez vous référer à un membre par son nom complet. Dans Glacerie::CornetDeGlace, ajouter_flocons_de_chocolat() fait référence à la même fonction que Glacerie::CornetDeGlace::ajouter_flocons_de_chocolat() à l'extérieur de l'espace de noms.

Alors que des règles standard de nommage s'appliquent aux noms des paquetages, par convention les noms des paquetages définis par l'utilisateur commencent tous par une majuscule. Perl réserve des noms en minuscules pour les paquetages pragmatiques de base (PragmasPragmas), comme strict et warnings. Cette politique est appliquée principalement par des directives communautaires.

Tous les espaces de noms en Perl sont visibles globalement. Quand Perl regarde un symbole dans Glacerie::CornetDeGlace::Congelateur, il recherche dans le tableau de symboles main:: un symbole représentant l'espace de noms Glacerie::, puis dans celui-ci, un espace de noms CornetDeGlace:: et ainsi de suite. Pourtant, Congelateur:: est visible de l'extérieur de l'espace de noms Glacerie::. Les espaces de noms sont accessibles globalement. L'imbrication du premier dans le dernier est seulement un mécanisme de stockage et n'implique rien de plus sur les relations entre les paquetages parents et enfants ou frères et sœurs.

Seul un programmeur peut rendre évidentes les relations logiques entre entités en choisissant de bons noms et en les organisant bien.

IV-B. Variables

En Perl, une variable est un emplacement de stockage pour une valeur (ValeursValeurs). Alors qu'un programme trivial peut manipuler directement les valeurs, la plupart des programmes travaillent avec des variables pour simplifier la logique du code. Une variable représente des valeurs ; il est plus facile d'expliquer le théorème de Pythagore avec des variables a, b et c que d'en déduire le principe en produisant une longue liste de valeurs valides. Ce concept peut sembler élémentaire, mais pour programmer efficacement, vous devez apprendre l'art d'équilibrer le générique et le réutilisable avec le spécifique.

IV-B-1. Portée des variables

Les variables sont accessibles dans votre programme en fonction de leur portée (PortéePortée). La plupart des variables que vous rencontrerez ont une portée lexicale (Portée lexicalePortée lexicale), ou portée définie par la syntaxe du programme tel qu'écrit. La plupart des portées lexicales sont soit le contenu des blocs délimités par des accolades ({ et }), soit des fichiers entiers. Les fichiers eux-mêmes fournissent leurs propres portées lexicales, si bien que la déclaration package à elle seule ne crée pas une nouvelle portée :

 
Sélectionnez
package Magasin::Jouet;

my $remise = 0.10;

package Magasin::Musique;

# $remise toujours visible
say "Notre remise courante est $remise !";

Depuis la version 5.14, vous pouvez également fournir un bloc à la déclaration package. Comme que cela introduit un nouveau bloc, cela fournit également une nouvelle portée lexicale :

 
Sélectionnez
package Magasin::Jouet
{
    my $remise = 0.10;
}

package Magasin::Musique
{
    # $remise pas disponible
}

package Magasin::Jeu;

# $remise toujours pas disponible

IV-B-2. Sigils de variables

Le sigil de la variable dans une déclaration détermine le type de la variable : scalaire, tableau ou hachage. Le sigil utilisé lors de l'accès d'une variable varie en fonction de ce que vous faites à la variable. Par exemple, vous déclarez un tableau @valeurs. Accédez au premier élément — une seule valeur — du tableau avec $valeurs[0]. Accédez à une liste de valeurs du tableau avec @valeurs[ @indices ]. Comme on pouvait s'y attendre, le sigil que vous utilisez détermine un contexte de quantité dans une situation de lvalue :

 
Sélectionnez
# impose le contexte lvalue à une_fonction()
@valeurs[ @indices ] = une_fonction()

... ou une conversion dans une situation rvalue :

 
Sélectionnez
# liste évaluée à l'élément final dans un contexte scalaire
my $element = @valeurs[ @indices ]

IV-B-3. Variables anonymes

Les variables Perl ne nécessitent pas de nom. Les noms existent pour aider le programmeur à garder trace d'une $pomme, de @tonneaux, ou de %recettes_gateaux. Les variables créées sans noms littéraux dans votre code source sont des variables anonymes. La seule façon d'accéder à des variables anonymes est par référence (RéférencesRéférences).

IV-B-4. Variables, types et coercition

Cette relation entre les types de variable, sigils et contexte est essentielle à votre compréhension de Perl.

Une variable Perl représente à la fois une valeur (coût en dollars, garnitures à pizza disponibles, noms et numéros des magasins de guitares) et le conteneur qui stocke cette valeur. Le système de type de Perl traite les types de valeurs et les types de conteneurs. Bien que le type de conteneur d'une variable — scalaire, tableau ou hachage — ne puisse pas changer, Perl est flexible quant au type de valeur de la variable. Vous pouvez stocker une chaîne dans une variable sur une ligne de code, ajouter un nombre à cette variable sur la prochaine ligne et réaffecter une référence à une fonction (Références de fonctionsRéférences de fonctions) sur la troisième. Mais vous allez vous embrouiller si vous faites tout cela...

Effectuer une opération sur une variable qui impose un type de valeur spécifique peut provoquer la coercition (CoercitionCoercition) du type de valeur existant de la variable.

Par exemple, la manière documentée pour déterminer le nombre d'éléments d'un tableau est d'évaluer ce tableau dans un contexte scalaire (ContexteContexte). Parce qu'une variable scalaire ne peut contenir qu'un scalaire, l'affectation d'un tableau à un scalaire impose un contexte scalaire à l'opération, et un tableau évalué dans un contexte scalaire produit le nombre d'éléments du tableau :

 
Sélectionnez
my $nombre_d_elements = @tableau;

IV-C. Valeurs

En acquérant de l'expérience, vous découvrirez que la structure de vos programmes dépendra de votre façon de modéliser vos données avec des variables.

Les variables permettent la manipulation abstraite de données tandis que les valeurs qu'elles contiennent rendent les programmes concrets et utiles. Plus vos valeurs sont précises, meilleurs seront vos programmes. Ces valeurs sont le nom et l'adresse de votre tante, la distance entre votre bureau et un terrain de golf sur la Lune, ou le poids de tous les gâteaux que vous avez mangés l'année dernière. Dans votre programme, les règles concernant le format des données sont souvent strictes.

Un programme efficace nécessite des façons efficaces (simples, rapides, pratiques, faciles à utiliser) de représenter ses données.

IV-C-1. Chaînes de caractères

Une chaîne de caractères est un fragment de données textuelles ou binaires sans formatage ou contenu particulier. Cela pourrait être votre nom, le contenu d'un fichier image ou le code source du programme lui-même. Une chaîne de caractères a un sens dans le programme uniquement si vous lui en donnez un.

Pour représenter une chaîne littérale dans votre programme, entourez-la par des délimiteurs de chaînes. Les délimiteurs de chaînes de caractères les plus courants sont des guillemets simples (ou apostrophes) et les guillemets doubles :

 
Sélectionnez
my $nom     = 'Donner Odinson, Bringer of Despair';
my $adresse = "Room 539, Bilskirnir, Valhalla";

Les caractères d'une chaîne entre guillemets simples sont exactement et uniquement ce qu'elles semblent être, à deux exceptions près. Pour inclure une apostrophe dans une chaîne entre guillemets simples, protégez-la en la précédant par un antislash (barre oblique inverse), qui est le caractère d'échappement :

 
Sélectionnez
my $reminder = 'N\'oublie pas d\'échapper '
             . 'le guillemet simple !';

Si vous voulez un antislash littéral à la fin de la chaîne, vous devrez l'échapper aussi, pour éviter que Perl ne pense que vous essayez d'échapper le dernier guillemet simple. La conception du langage de programmation est pleine de cas comme celui-ci :

 
Sélectionnez
my $exception = 'Cette chaîne se termine par un '
              . 'antislash, pas par un guillemet : \\';

Tout autre antislash fera partie de la chaîne tel quel, sauf si vous le doublez, auquel cas Perl croira que vous voulez échapper le second :

 
Sélectionnez
is('Modern \ Perl', 'Modern \\ Perl',
    'échapper les antislash entre guillemets simples');

Une chaîne de caractères entre guillemets doubles vous offre plus d'options. Par exemple, vous pouvez y encoder des caractères autrement invisibles :

 
Sélectionnez
my $tab       = "\t";  # tabulation
my $newline   = "\n";  # retour à la ligne
my $carriage  = "\r";  # retour chariot
my $formfeed  = "\f";  # saut de ligne
my $backspace = "\b";  # retour arrière

Cela illustre un principe utile : il existe plusieurs représentations possibles de la même chaîne. Vous pouvez inclure une tabulation dans une chaîne en tapant la séquence d'échappement \t ou en appuyant sur la touche Tab de votre clavier. En Perl, les deux chaînes se comportent de la même manière, même si la représentation de la chaîne peut différer dans le code source.

La déclaration d'une chaîne peut traverser (et inclure) des sauts de ligne, alors ces deux déclarations sont équivalentes :

 
Sélectionnez
my $escaped = "deux\nlignes";
my $literal = "deux
lignes";
is $escaped, $literal, 'chaînes équivalentes';

Cela dit, les séquences d'échappement sont souvent beaucoup plus lisibles que leurs équivalents littéraux.

Lorsque vous manipulez et modifiez des chaînes, Perl va adapter leur taille selon le cas ; ces chaînes ont des longueurs variables. Par exemple, vous pouvez combiner plusieurs chaînes dans une plus longue avec l'opérateur de concaténation :

 
Sélectionnez
my $chaton = 'Choco' . ' ' . 'Spidermonkey';

… ce qui est de fait la même chose que si vous aviez initialisé la chaîne en une seule fois.

Vous pouvez également interpoler la valeur d'une variable scalaire ou les valeurs d'un tableau dans une chaîne entourée par des guillemets doubles, de sorte que le contenu actuel de la variable fasse partie de la chaîne comme si vous l'aviez concaténé :

 
Sélectionnez
my $info = "$nom habite à $adresse !";

# équivalent à
my $info = $nom . ' habite à ' . $adresse . ' !';

Pour insérer des guillemets doubles dans une chaîne entre guillemets, protégez-les en les préfixant d'un antislash :

 
Sélectionnez
my $quote = "\"Aïe,\", cria-t-il.  \"Ça fait mal !\"";

Quand l'ajout répété des antislash devient lourd, utilisez un opérateur de citation, qui vous permet de choisir un autre délimiteur pour la chaîne. L'opérateur q indique les guillemets simples (pas d'interpolation), tandis que l'opérateur qq fournit un comportement de guillemets doubles (interpolation). Le caractère qui suit immédiatement l'opérateur détermine les caractères utilisés pour délimiter les chaînes. Si c'est le caractère d'ouverture d'une paire équilibrée de caractères, comme les accolades ouvrante et fermante, le caractère fermant sera le délimiteur de fin. Sinon, le caractère lui-même sera à la fois le délimiteur de début et de fin.

 
Sélectionnez
my $quote     = qq{"Aïe", dit-il. "Ça fait mal !"};
my $reminder  =  q^N'échappe pas l'apostrophe !^;
my $complaint =  q{C'est trop tôt pour être réveillé.};

Lorsque la déclaration d'une chaîne complexe contenant une série d'échappements est fastidieuse, utilisez la syntaxe heredoc (parfois appelée « document ici même ») pour assigner plusieurs lignes à une chaîne :

 
Sélectionnez
my $paragraphe =<<'FIN_PARA';

Et Caïn répondit : "Non, il est toujours ."
Alors il dit: "je veux habiter sous la terre
Comme dans son sépulcre un homme solitaire ;
Rien ne me verra plus, je ne verrai plus rien."
On fit donc une fosse, et Caïn dit "C'est bien !"
Puis il descendit seul sous cette voûte sombre.
Quand il se fut assis sur sa chaise dans l'ombre
Et qu'on eut sur son front fermé le souterrain,
L'œil était dans la tombe et regardait Caïn.
FIN_PARA

La syntaxe <<'FIN_PARA' comporte trois parties. Les doubles chevrons introduisent le document de type heredoc. Les guillemets déterminent si celui-ci suit un comportement de guillemets simples ou doubles. (En l'absence de guillemets, le comportement par défaut est celui des guillemets doubles.) FIN_PARA est un identifiant arbitraire que l'analyseur Perl utilise comme délimiteur de fin.

Quelle que soit l'indentation de la déclaration heredoc, le délimiteur de fin doit commencer au début de la ligne :

 
Sélectionnez
sub une_fonction {
    my $ingredients =<<'END_INGREDIENTS';
    Deux œufs
    Une tasse de farine
    Deux noix de beurre
    Un quart de cuillère à café de sel
    Une tasse de lait
    Une goutte de vanille
    Assaisonner selon le goût
END_INGREDIENTS
}

Si l'identifiant commence par un espace, le même espace doit être présent avant le séparateur de fin, c'est-à-dire, <<' END_HEREDOC'>> a besoin d'un espace devant END_HEREDOC. Même si vous indentez l'identifiant, Perl ne va pas supprimer les espaces équivalents du début de chaque ligne de la heredoc. Oui, c'est loin d'être idéal.

L'utilisation d'une chaîne de caractères en dehors d'un contexte de chaîne engendra une coercition (CoercitionCoercition).

IV-C-2. Unicode et chaînes de caractères

Unicode est un système pour représenter les caractères des langues écrites du monde. Alors que la majorité du texte anglais utilise un ensemble de seulement 127 caractères (ce qui nécessite sept bits de mémoire et s'insère facilement dans des octets de huit bits), il serait naïf de croire que vous n'aurez pas besoin d'un tréma un jour.

En Perl, les chaînes de caractères peuvent représenter l'un des deux types de données distincts, mais connexes :

  • séquences de caractères Unicode : chaque caractère a un codepoint ou point de code, un numéro unique qui l'identifie dans le jeu de caractères Unicode ;
  • séquences d'octets : les données binaires sont une séquence d'octets — suites de huit bits, dont chacun peut représenter un nombre compris entre 0 et 255.

Les mots sont importants

Pourquoi octet et pas byte ? Supposer qu'un caractère rentre dans un byte ne mettra pas fin au souci d'Unicode. Séparez l'idée de stockage en mémoire de la représentation du caractère. Oubliez que vous avez déjà entendu parler de bytes.

Les chaînes Unicode et les chaînes binaires se ressemblent superficiellement. Chacune a une length(). Chacune supporte les opérations de chaîne standard comme la concaténation, le prélèvement d'un fragment de la chaîne et le traitement des expressions régulières (Expressions régulières et correspondanceExpressions régulières et correspondance). Toute chaîne qui ne contient pas des données purement binaires contient des données textuelles et devrait donc être une séquence de caractères Unicode.

Cependant, en raison de la façon dont votre système d'exploitation représente les données sur disque ou sur le réseau — sous forme de séquences d'octets — Perl ne peut pas savoir si les données que vous lisez représentent une image, un document texte ou autre chose. Par défaut, Perl traite toutes les données entrantes comme des séquences d'octets. C'est à vous d'ajouter un sens spécifique à ces données.

IV-C-2-a. Codage des caractères

Une chaîne Unicode est une séquence d'octets qui représente une séquence de caractères. Un codage Unicode mappe des séquences d'octets vers des caractères ou graphèmes. Certains codages, comme UTF-8, peuvent coder tous les caractères du jeu de caractères Unicode. D'autres encodages représentent uniquement un sous-ensemble de caractères Unicode. Par exemple, ASCII encode le texte brut anglais sans caractères accentués, tandis que Latin-1 peut représenter du texte dans la plupart des langues qui utilisent l'alphabet latin.

Un standard en évolution

Perl 5.14 supporte le standard Unicode 6.0, 5.16 la norme 6.1 et 5.18 la norme 6.2. Voir http://unicode.org/versions/.

Pour éviter la plupart des problèmes Unicode, décodez toujours depuis et vers le codage approprié les entrées et sorties de votre programme.

IV-C-2-b. Unicode dans vos descripteurs de fichiers

Quand vous dites à Perl qu'un descripteur de fichier spécifique (FichiersFichiers) doit traiter des données avec un encodage Unicode spécifique, Perl utilisera une couche d'entrées-sorties pour faire la conversion entre octets et caractères. L'opérande mode de la commande interne open vous permet de demander une couche d'entrée-sorties en fournissant son nom. Par exemple, la couche :utf8 décode les données UTF-8 :

 
Sélectionnez
open my $fh, '<:utf8', $textfile;

my $unicode_string = <$fh>;

Utilisez binmode pour appliquer une couche d'E/S à un descripteur de fichier existant :

 
Sélectionnez
binmode $fh, ':utf8';
my $unicode_string = <$fh>;

binmode STDOUT, ':utf8';
say $unicode_string;

Sans le mode utf8, l'impression de certaines chaînes Unicode dans un descripteur de fichier se traduira par un avertissement (Wide character in %s), parce que les fichiers contiennent des octets et pas des caractères Unicode.

Activer UTF-8 partout

Le module utf8::all active les couches UTF-8 sur tous les descripteurs de fichiers de l'ensemble de votre programme et permet toutes sortes d'autres fonctionnalités Unicode. Cela est très pratique, mais cela ne vous dispense pas (éventuellement) de réfléchir et de déterminer ce dont a besoin votre programme.

IV-C-2-c. Unicode dans vos données

Le module standard Encode fournit une fonction nommée decode() pour convertir un scalaire contenant des octets vers la version de chaînes Unicode interne à Perl. La fonction encode() correspondante convertit à partir du codage interne de Perl vers le codage désiré :

 
Sélectionnez
my $from_utf8 = decode('utf8', $data);
my $to_latin1 = encode('iso-8859-1', $string);

Pour gérer correctement Unicode, vous devez toujours décoder les données entrantes avec un codage connu et encoder les données sortantes vers un codage connu. Oui, cela signifie que vous devez savoir quel type de données vous envoyez et recevez, mais de toute façon vous devriez le savoir. Être spécifique vous aidera à éviter toutes sortes d'ennuis.

IV-C-2-d. Unicode dans vos programmes

Vous pouvez inclure des caractères Unicode dans vos programmes de trois façons. Le plus simple est d'utiliser le pragma utf8 (PragmasPragmas), qui indique à l'analyseur Perl d'interpréter le reste du fichier de code source avec le codage UTF-8. Cela vous permet d'utiliser des caractères Unicode dans des chaînes et identifiants :

 
Sélectionnez
use utf8;

sub £_to_¥ { ... }

my $yen = £_to_¥('1000£');

Pour écrire ce code, votre éditeur de texte doit comprendre UTF-8 et vous devez enregistrer le fichier avec le codage approprié. Encore une fois, deux programmes qui communiquent par des données Unicode doivent se mettre d'accord sur le codage de ces données.

Dans les chaînes entre guillemets doubles, vous pouvez utiliser la séquence d'échappement Unicode pour représenter le codage des caractères. La syntaxe \x{} représente un caractère unique ; placez la valeur hexadécimale du caractère Unicode (voir http://unicode.org/charts/ pour une liste exhaustive) entre les accolades :

 
Sélectionnez
my $escaped_thorn = "þ";

Certains caractères Unicode ont des noms, et ces noms sont souvent plus lisibles que leur code hexadécimal, même s'ils sont beaucoup plus longs. Utilisez le pragma charnames pour les activer et la séquence d'échappement \N{} pour les désigner :

 
Sélectionnez
use charnames ':full';
use Test::More tests => 1;

my $escaped_thorn = "þ";
my $named_thorn   = "\N{LATIN SMALL LETTER THORN}";

is $escaped_thorn, $named_thorn,
    'Thorn equivalence check';

Vous pouvez utiliser les formats \x{} et \N{} dans les expressions régulières ainsi que partout où vous pouvez légitimement utiliser une chaîne ou un caractère.

IV-C-2-e. Conversion implicite

La plupart des problèmes Unicode en Perl découlent du fait qu'une chaîne peut être soit une séquence d'octets, soit une séquence de caractères. Perl vous permet de combiner ces types par le biais des conversions implicites. Lorsque ces conversions sont erronées, elles sont rarement évidemment erronées et souvent spectaculairement erronées, de façon à être difficiles à déboguer.

Lorsque Perl concatène une séquence d'octets avec une séquence de caractères Unicode, il décode implicitement la séquence d'octets en utilisant le codage Latin-1. La chaîne qui en résulte contient des caractères Unicode. Lorsque vous affichez des caractères Unicode, Perl va encoder la chaîne en utilisant UTF-8, car Latin-1 ne peut pas représenter l'ensemble des caractères Unicode — Latin-1 est un sous-ensemble d'UTF-8.

L'asymétrie entre codages et octets peut conduire à des chaînes Unicode encodées en UTF-8 sur la sortie et décodées comme Latin-1 en entrée. Encore pire, lorsque le texte ne contient que des caractères anglais sans accent, le bogue reste caché, parce que les deux codages utilisent la même représentation pour chaque caractère.

 
Sélectionnez
my $hello    = "Hello, ";
my $greeting = $hello . $nom;

Si $nom contient un nom comme Alice vous ne remarquerez aucun problème, car la représentation Latin-1 est identique à la représentation UTF-8. Si $nom contient un nom tel que José, $nom peut contenir plusieurs valeurs possibles :

  • $nom contient quatre caractères Unicode ;
  • $nom contient quatre octets Latin-1 représentant les quatre caractères Unicode ;
  • $nom contient cinq octets UTF-8 représentant les quatre caractères Unicode.

Pour une chaîne de caractères, il y a plusieurs scénarios possibles :

  • c'est une chaîne de caractères ASCII et contient des octets ;
 
Sélectionnez
my $hello = "Hello, ";
  • c'est une chaîne de caractères Latin-1 sans codage explicite et contient des octets. La chaîne littérale contient des octets ;
 
Sélectionnez
my $hello = "¡Hola, ";
  • c'est une chaîne littérale non-ASCII avec pragma utf8 ou encoding en vigueur et contient des caractères Unicode.
 
Sélectionnez
use utf8;
my $hello = "Kuirabá, ";

Si tant $hello que $nom sont des chaînes Unicode, leur concaténation va produire une autre chaîne Unicode.

Si les deux chaînes sont des flux d'octets, Perl les concaténera dans une nouvelle chaîne d'octets. Si les deux valeurs sont des octets ayant le même codage, par exemple Latin-1, la concaténation fonctionnera correctement. Si les octets n'ont pas le même codage, par exemple une concaténation ajoutant des données UTF-8 à des données Latin-1, la séquence d'octets résultante n'aura pas de sens dans aucun des deux encodages. Cela pourrait se produire si l'utilisateur a entré un nom au format UTF-8 et la formule de salutation (contenu de la variable $hello) sous la forme d'une chaîne Latin-1, mais le programme n'a rien décodé.

Si seulement une des valeurs est une chaîne Unicode, Perl décodera l'autre comme des données Latin-1. Si ce n'est pas l'encodage correct, les caractères Unicode en résultant seront erronés. Par exemple, si l'entrée de l'utilisateur contenait des données UTF-8 et la chaîne littérale était en Unicode, le nom pourrait être décodé incorrectement en cinq caractères Unicode pour former José (sic) à la place de José parce que les données UTF-8 signifient autre chose lors du décodage que les données Latin-1.

Si votre tête tourne, vous n'êtes pas seul. Il faut toujours décoder l'entrée et encoder la sortie.

Voir perldoc perluniintro pour une explication beaucoup plus détaillée sur l'Unicode, les encodages et comment gérer les données entrantes et sortantes dans un monde Unicode. Pour beaucoup plus de détails concernant la gestion Unicode efficace tout au long de vos programmes, voir la réponse de Tom Christiansen à la question « Pourquoi Modern Perl évite l'encodage UTF-8 par défaut ? » http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/6163129#6163129 et sa série « Perl Unicode Cookbook » sur Perl.com http://www.perl.com/pub/2012/04/perlunicook-standard-preamble.html.

Perl 5.12 a ajouté une fonctionnalité, unicode_strings, qui active la sémantique Unicode pour toutes les opérations de chaîne à l'intérieur de sa portée. Perl 5.14 a amélioré cette fonctionnalité et Perl 5.16 l'a parachevée. Si vous travaillez avec Unicode en Perl, vous devez utiliser au moins Perl 5.14 et, idéalement, Perl 5.18.

IV-C-3. Nombres

Perl prend en charge tant les nombres entiers que les valeurs à virgule flottante. Vous pouvez les représenter avec la notation scientifique ainsi qu'en formats binaire, octal et hexadécimal :

 
Sélectionnez
my $integer   = 42;
my $float     = 0.007;
my $sci_float = 1.02e14;
my $binary    = 0b101010;
my $octal     = 052;
my $hex       = 0x20;

Les caractères en gras sont les préfixes numériques pour la notation binaire, octale et hexadécimale, respectivement. Soyez conscient qu'un zéro au début d'un nombre entier indique toujours le mode octal.

Quand 1,99 + 1,99 = 4

Même si vous pouvez écrire explicitement des valeurs à virgule flottante avec une parfaite exactitude, Perl — comme la plupart des langages de programmation — les représente en interne dans un format binaire. Cette représentation est parfois imprécise de manière spécifique ; consultez perldoc perlnumber pour plus de détails.

Vous ne pouvez pas utiliser des virgules pour séparer les milliers dans les valeurs numériques, car l'analyseur les interprétera comme l'opérateur virgule. Au lieu de cela, utilisez le caractère de soulignement dans le nombre. L'analyseur le traitera comme un caractère invisible. Ainsi, toutes ces expressions sont équivalentes, mais la deuxième pourrait être la plus lisible :

 
Sélectionnez
my $milliard = 1000000000;
my $milliard = 1_000_000_000;
my $milliard = 10_0_00_00_0_0_0;

En raison de la coercition (CoercitionCoercition), les programmeurs Perl ont rarement à se soucier de devoir convertir en nombres les données provenant de l'extérieur du programme. Perl traitera tout ce qui ressemble à un nombre comme un nombre lorsqu'il est évalué dans un contexte numérique. Dans les rares cas où vous avez besoin de savoir si quelque chose ressemble à un nombre sans l'évaluer dans un contexte numérique, utilisez la fonction looks_like_number du module standard Scalar::Util. Cette fonction renvoie une valeur vraie si Perl estime que l'argument donné est numérique.

Le module Regexp::Common du CPAN fournit plusieurs expressions régulières éprouvées pour identifier les types plus spécifiques de valeurs numériques tels que les nombres entiers, les entiers et les valeurs à virgule flottante.

IV-C-4. Undef

La valeur undef de Perl représente une valeur non attribuée, indéfinie et inconnue. Les variables scalaires déclarées, mais non initialisées contiennent undef :

 
Sélectionnez
my $nom = undef;   # affectation inutile
my $rang;          # contient également undef

undef est évalué à faux dans un contexte booléen. L'évaluation de undef dans un contexte de chaîne de caractères — comme l'interpolation dans une chaîne :

 
Sélectionnez
my $indefini;
my $defini = $indefini . '... et ainsi de suite';

… génère un avertissement uninitialized value :

Use of uninitialized value $undefined in concatenation (.) or string...

La commande interne defined renvoie une valeur vraie si son opérande est évalué à une valeur définie (c'est-à-dire autre chose que undef) :

 
Sélectionnez
my $statut = 'souffrant d\'un rhume';

say defined $statut;  # 1, qui représente une valeur vraie
say defined undef;    # chaîne vide; une valeur fausse

IV-C-5. La liste vide

Lorsqu'elle est utilisée du côté droit d'une affectation, la construction () représente une liste vide. Dans un contexte scalaire, elle est évaluée à undef. Dans un contexte de liste, c'est une liste vide. Lorsqu'elle est utilisée du côté gauche d'une affectation, la construction () impose le contexte de liste. Pourquoi voudriez-vous faire cela ? Pour compter le nombre d'éléments retournés par une expression dans un contexte de liste sans utiliser une variable temporaire, utilisez l'expression idiomatique (Expressions idiomatiquesExpressions idiomatiques) :

 
Sélectionnez
my $count = () = get_clown_hats();

En raison de l'associativité à droite (AssociativitéAssociativité) de l'opérateur d'affectation, Perl évalue d'abord la deuxième affectation en appelant get_clown_hats() dans un contexte de liste. Cela produit une liste.

L'affectation à la liste vide efface toutes les valeurs de la liste, mais l'affectation a lieu dans un contexte scalaire, qui évalue le nombre d'éléments du côté droit de l'affectation. Par conséquent, $count contient le nombre d'éléments de la liste retournée par get_clown_hats().

Compliqué ? Cela peut embrouiller les nouveaux programmeurs, mais avec la pratique, vous verrez comment s'emboîtent les caractéristiques fondamentales de conception de Perl.

IV-C-6. Listes

Une liste est un groupe d'une ou plusieurs expressions séparées par des virgules. Les listes peuvent se trouver textuellement dans le code source comme des valeurs :

 
Sélectionnez
my @premiers_fibs = (1, 1, 2, 3, 5, 8, 13, 21);

… comme des cibles d'affectations :

 
Sélectionnez
my ($package, $filename, $line) = caller();

… ou comme des listes d'expressions :

 
Sélectionnez
say name(), ' => ', age();

Les parenthèses ne créent pas des listes. L'opérateur virgule crée des listes. Là où elles sont présentes, les parenthèses dans ces exemples groupent des expressions pour changer leur priorité (Ordre des opérations (priorité)Ordre des opérations (priorité)).

Utilisez l'opérateur d'intervalle pour créer des listes dans une forme compacte. Voyez-vous ? Des listes, mais pas de parenthèses :

 
Sélectionnez
my @chars = 'a' .. 'z';
my @count = 13 .. 27;

Utilisez l'opérateur qw() pour diviser une chaîne littérale sur les espaces et produire une liste de chaînes entre parenthèses, mais vous pouvez utiliser n'importe quel délimiteur, comme qw :

 
Sélectionnez
my @stooges = qw( Larry Curly Moe Shemp Joey Kenny );

Pas de commentaires s'il vous plaît

Perl émet un avertissement si un qw() contient une virgule ou le caractère de commentaire (#), non seulement parce que ces caractères sont rares dans un qw(), mais aussi parce que leur présence est souvent une erreur.

Les listes peuvent (souvent) apparaître en tant que résultats d'expressions, mais ces listes n'apparaissent pas littéralement dans le code source.

Les listes et les tableaux ne sont pas interchangeables en Perl. Vous pouvez stocker une liste dans un tableau et vous pouvez contraindre un tableau vers une liste, mais les listes et les tableaux sont des concepts distincts. Les listes sont des valeurs. Les tableaux sont des conteneurs. Par exemple, l'indexation dans une liste survient toujours dans un contexte de liste. L'indexation dans un tableau peut se produire dans un contexte scalaire (pour un seul élément) ou dans un contexte de liste (pour une opération de tranche) :

 
Sélectionnez
# ne vous souciez pas des détails pour l'instant
sub context
{
    my $context = wantarray();

    say defined $context
         ? $context
             ? 'list'
             : 'scalar'
         : 'void';
    return 0;
}

my @list_slice  = (1, 2, 3)[context()];
my @array_slice = @list_slice[context()];
my $array_index = $array_slice[context()];

say context(); # contexte de liste
context();     # contexte void

IV-D. Flot d'exécution

Le flot d'exécution principal de Perl est simple. L'exécution du programme commence au début (la première ligne du fichier exécuté) et continue jusqu'à la fin :

 
Sélectionnez
say 'Au début';
say 'Au milieu';
say 'A la fin';

Les commandes de contrôle du flux d'exécution (instructions de saut conditionnelles ou inconditionnelles, boucles, appels de fonctions, etc.) de Perl changent l'ordre d'exécution — c'est-à-dire, ce qui se passe ensuite dans le programme.

IV-D-1. Les instructions conditionnelles

L'instruction if effectue l'action associée uniquement lorsque son expression conditionnelle est évaluée à une valeur true :

 
Sélectionnez
say 'Hello, Bob!' if $nom eq 'Bob';

Cette forme postfixée est utile pour des expressions simples. La forme avec bloc de l'instruction if regroupe des instructions multiples dans une unité qui est exécutée si la condition est vraie :

 
Sélectionnez
if ($nom eq 'Bob')
{
    say 'Hello, Bob!';
    found_bob();
}

Tandis que la forme avec bloc requiert des parenthèses autour de sa condition, la forme postfixée n'en nécessite pas.

L'expression conditionnelle peut consister en plusieurs sous-expressions, tant qu'elle est évaluée vers quelque chose qui peut être converti en une valeur booléenne :

 
Sélectionnez
if ($nom eq 'Bob' && not greeted_bob())
{
    say 'Hello, Bob!';
    found_bob();
}

Dans la forme postfixée, l'ajout des parenthèses peut clarifier l'intention du code au détriment de la concision et de la propreté visuelle :

 
Sélectionnez
greet_bob() if ($nom eq 'Bob' && not greeted_bob());

L'instruction unless (mot qui signifie « à moins que », « sauf si ») est la forme inversée (négative) de if. Perl exécutera l'action lorsque l'expression conditionnelle est évaluée à une valeur fausse :

 
Sélectionnez
say "Vous n'êtes pas Bob !!" unless $nom eq 'Bob';

Comme if, unless dispose également d'une forme de bloc, bien que beaucoup de programmeurs l'évitent, en raison de son potentiel de confusion :

 
Sélectionnez
unless (is_annee_bissextile() and is_pleine_lune())
{
    cabriole();
    gambade();
}

L'opérateur unless fonctionne très bien pour les expressions conditionnelles postfixées, en particulier la validation des paramètres des fonctions (Validation postfixée des paramètresValidation postfixée des paramètres) :

 
Sélectionnez
sub cabriole
{
    # ne rien faire sans paramètres
    return unless @_;

    for my $chant (@_) { ... }
}

Les formes avec blocs de if et unless admettent toutes les deux l'instruction else, qui fournit le code à exécuter lorsque l'expression conditionnelle n'est pas évaluée à la valeur vraie ou fausse appropriée :

 
Sélectionnez
if ($nom eq 'Bob')
{
    say 'Salut, Bob!';
    saluer_user();
}
else
{
    say "Je ne vous connais pas.";
    eviter_user();
}

Les blocs else vous permettent de réécrire des instructions conditionnelles if et unless l'une à la place de l'autre :

 
Sélectionnez
unless ($nom eq 'Bob')
{
    say "Je ne vous connais pas.";
    eviter_user();
}
else
{
    say 'Salut, Bob!';
    saluer_user();
}

Toutefois, la double négation implicite dans l'utilisation de unless avec un bloc else peut prêter à la confusion. Cet exemple est peut-être le seul endroit où vous voyez cela de toute votre vie de programmeur.

Tout comme il fournit if et unless pour vous permettre de formuler vos conditions de la manière la plus lisible, Perl a deux opérateurs conditionnels positif et négatif :

 
Sélectionnez
if ($nom ne 'Bob')
{
    say "Je ne vous connais pas.";
    eviter_user();
}
else
{
    say 'Salut, Bob!';
    saluer_user();
}

… bien que la double négation qu'implique la présence du bloc else puisse être difficile à lire.

Si vous avez beaucoup de conditions à vérifier et si elles sont mutuellement exclusives, utilisez une ou plusieurs instructions elsif :

 
Sélectionnez
if ($nom eq 'Bob')
{
    say 'Salut, Bob!';
    saluer_user();
}
elsif ($nom eq 'Jim')
{
    say 'Salut, Jim!';
    saluer_user();
}
else
{
    say "Vous n'êtes pas mon oncle.";
    eviter_user();
}

Une chaîne unless peut également utiliser un bloc elsif. Bonne chance pour déchiffrer cela ! Il n'y a pas de elseunless.

Écrire else if est une erreur de syntaxe. Larry préfère elsif pour des raisons esthétiques, comme dans le langage de programmation Ada :

 
Sélectionnez
if ($nom eq 'Rick')
{
    say 'Salut, cousin !';
}

# avertissement; erreur de syntaxe
else if ($nom eq 'Kristen')
{
    say 'Salut, cousine !';
}

IV-D-2. L'opérateur conditionnel ternaire

L'opérateur conditionnel ternaire évalue une première expression conditionnelle et renvoie selon le cas la deuxième ou la troisième sous-expression :

 
Sélectionnez
my $time_suffix = apres_midi($time)
                ? 'après-midi'
                : 'matin';

L'expression conditionnelle précède le point d'interrogation (?) et le caractère deux-points (:) sépare les deux termes de l'alternative. Celles-ci sont des expressions de complexité arbitraire, y compris d'autres expressions conditionnelles ternaires.

Un idiome intéressant, quoique quelque peu obscur, consiste à utiliser l'opérateur conditionnel ternaire pour choisir entre deux variables, non seulement entre deux valeurs :

 
Sélectionnez
push @{ rand() > 0.5 ? \@red_team : \@blue_team },
    Player->new;

Encore une fois, pesez les avantages de la clarté par rapport à ceux de la concision.

IV-D-2-a. Court-circuit

Perl présente un comportement de court-circuit quand il rencontre des expressions conditionnelles complexes. Lorsque Perl peut déterminer si une expression complexe réussira ou échouera dans son ensemble sans évaluer chaque sous-expression, il n'évaluera pas les sous-expressions ultérieures. Cela est particulièrement évident avec un exemple :

 
Sélectionnez
say 'Les deux sont vraies !' if ok( 1, 'sous-expression 1' )
                             && ok( 1, 'sous-expression 2' );

done_testing();

La valeur de retour de ok() (TestsTests) est la valeur booléenne produite par le premier argument, donc l'exemple affiche :

ok 1 - sous-expression 1

ok 2 - sous-expression 2

Les deux sont vraies !

Lorsque la première sous-expression — le premier appel à ok — est évaluée à une valeur vraie, Perl doit évaluer la seconde sous-expression. Si la première sous-expression avait été évaluée à faux, il n'y aurait aucun besoin de vérifier les sous-expressions suivantes, puisque l'expression entière ne peut pas réussir :

 
Sélectionnez
say 'Les deux sont vraies !' if ok( 0, 'sous-expression 1' )
                             && ok( 1, 'sous-expression 2' );

Cet exemple affiche :

not ok 1 - sous-expression 1

Même si la deuxième sous-expression devait à l'évidence réussir, Perl ne l'évalue pas. Le même comportement de court-circuit est évident pour opérations ou logique :

 
Sélectionnez
say 'L\'une ou l\'autre est vraie !' if ok( 1, 'sous-expression 1' )
                                     || ok( 1, 'sous-expression 2' );

Cet exemple affiche :

ok 1 - sous-expression 1

L'une ou l'autre est vraie !

Avec la réussite de la première sous-expression, Perl peut éviter d'en évaluer la seconde. Si la première sous-expression était fausse, le résultat de l'évaluation de la seconde dicterait le résultat de l'évaluation de l'expression dans son ensemble.

En plus de permettre d'éviter les calculs potentiellement coûteux, le comportement de court-circuit peut vous aider à éviter des erreurs et avertissements, comme dans le cas où l'utilisation d'une valeur non initialisée pourrait générer un avertissement :

 
Sélectionnez
my $bbq;
if (defined $bbq and $bbq eq 'poitrine_de_boeuf') { ... }

IV-D-3. Contexte des instructions conditionnelles

Les instructions conditionnelles — if, unless — et l'opérateur conditionnel ternaire — évaluent une expression dans le contexte booléen (ContexteContexte). Les opérateurs de comparaison tels que eq, ==, ne, et != produisent tous des résultats booléens lors de leur évaluation, mais Perl convertit les résultats des autres expressions — variables et valeurs y comprises — en format booléen.

Perl n'a pas une seule valeur vraie ou fausse. Tout nombre évalué à 0 est faux. Cela inclut 0, 0.0, 0e0, 0x0, et ainsi de suite. La chaîne vide ('') et la chaîne nulle '0' sont évaluées à faux, mais pas les chaînes '0.0', '0e0' et ainsi de suite. L'idiome '0 but true' prend la valeur 0 dans un contexte numérique, mais la valeur vraie dans un contexte booléen, grâce à son contenu de type chaîne.

Tant la liste vide que la valeur undef sont évaluées à faux. Des tableaux et tables de hachage vides renvoient le nombre 0 dans un contexte scalaire, donc ils sont évalués à une valeur fausse dans un contexte booléen. Un tableau qui contient un seul élément — même undef — est évalué à vrai dans le contexte booléen. Une table de hachage qui contient des éléments — même une clé et une valeur undef — est évaluée à une valeur vraie dans un contexte booléen.

Plus de contrôle sur le contexte

Le module Want du CPAN vous permet de détecter un contexte booléen dans vos propres fonctions. Le pragma standard overloading (SurchargeSurcharge) vous permet de spécifier ce que produisent vos propres types de données lors de l'évaluation dans des contextes différents.

IV-D-4. Boucles

Perl fournit plusieurs instructions de boucles et d'itérations. La boucle de type foreach évalue une expression qui produit une liste et exécute une instruction ou un bloc jusqu'à ce qu'elle ait épuisé la liste :

 
Sélectionnez
foreach (1 .. 10)
{
    say "$_ * $_ = ", $_ * $_;
}

Cet exemple utilise l'opérateur d'intervalle pour produire une liste d'entiers d'un à dix inclusivement. L'instruction foreach boucle dessus et à chaque étape donne cette valeur à la variable $_ (La variable scalaire par défautLa variable scalaire par défaut). Perl exécute le bloc pour chaque entier et, en conséquence, affiche les carrés des entiers.

foreach versus for

Nombreux programmeurs Perl se réfèrent à l'itération comme boucle foreach, mais Perl traite les noms foreach et for de façon interchangeable. C'est l'expression entre parenthèses qui détermine le type et le comportement de la boucle, pas le mot clé.

Comme if et unless, cette boucle a une forme postfixée :

 
Sélectionnez
say "$_ * $_ = ", $_ * $_ for 1 .. 10;

Une boucle for peut utiliser une variable nommée à la place de celle par défaut :

 
Sélectionnez
for my $i (1 .. 10)
{
    say "$i * $i = ", $i * $i;
}

Lorsqu'une boucle for utilise une variable d'itération nommée, la portée de celle-ci est à l'intérieur de la boucle. Perl fera correspondre la valeur de la variable à la valeur de chaque élément de l'itération. Perl ne modifiera pas la variable par défaut ($_). Si vous avez déclaré ailleurs dans le programme une variable nommée $i, sa valeur persistera en dehors de la boucle :

 
Sélectionnez
my $i = 'vache';

for my $i (1 .. 10)
{
    say "$i * $i = ", $i * $i;
}

is( $i, 'vache', 'Valeur conservée dans la portée extérieure' );

Cette localisation se produit même si vous ne déclarez pas la variable d'itération comme une variable lexicale (avec l'opérateur « my »)... mais déclarez vos variables d'itération comme lexicales pour réduire leur portée :

 
Sélectionnez
my $i = 'cheval';

for $i (1 .. 10)
{
    say "$i * $i = ", $i * $i;
}

is( $i, 'cheval', 'Valeur conservée dans la portée extérieure' );

IV-D-5. Itération et aliasing

La boucle for fait de la variable d'itération un alias des valeurs dans l'itération de telle sorte que toute modification de la valeur de l'itérateur modifie la valeur itérée courante :

 
Sélectionnez
my @nums = 1 .. 10;

$_ **= 2 for @nums;

is( $nums[0], 1, '1 * 1 is 1' );
is( $nums[1], 4, '2 * 2 is 4' );

...

is( $nums[9], 100, '10 * 10 is 100' );

Cet aliasing fonctionne aussi avec les boucles for avec bloc :

 
Sélectionnez
for my $num (@nums)
{
    $num **= 2;
}

… ainsi que pour l'itération avec la variable par défaut :

 
Sélectionnez
for (@nums)
{
    $_ **= 2;
}

Cependant, vous ne pouvez pas utiliser l'aliasing pour modifier des valeurs constantes. Perl lèvera une exception sur la modification des valeurs en lecture seule.

 
Sélectionnez
$_++ and say for qw( Huex Dewex Louid );

Vous pouvez voir parfois l'utilisation de for avec une seule variable scalaire :

 
Sélectionnez
for ($user_input)
{
    s/\A\s+//;      # supprime les espaces blancs au début
    s/\s+\z//;      # supprime les espaces blancs à la fin

    $_ = quotemeta; # escape des caractères non-word
}

Cet idiome (Expressions idiomatiquesExpressions idiomatiques) utilise l'opérateur d'itération pour son effet secondaire d'aliasing de $_. Généralement, il est plus clair d'opérer sur la variable nommée elle-même.

IV-D-6. Itération et étendue de la portée

La portée de l'itérateur de la variable par défaut présente un piège subtil. Considérons une fonction topic_mangler() qui volontairement modifie $_. Si le code itère sur une liste appelée @values sans protéger $_, vous allez passer quelque temps à déboguer les effets :

 
Sélectionnez
for (@values)
{
    topic_mangler();
}

sub topic_mangler
{
    s/foo/bar/;
}

Oui, la substitution dans topic_mangler() modifiera les éléments de @values. Si vous devez utiliser $_ plutôt qu'une variable nommée, utilisez le comportement de aliasing de la variable par défaut dans la boucle for :

 
Sélectionnez
sub topic_mangler
{
    # was $_ = shift;
    for (shift)
    {
        s/foo/bar/;
        s/baz/quux/;
        return $_;
    }
}

Comme substitut, utilisez une variable d'itération nommée dans la boucle for. C'est presque toujours le bon conseil.

IV-D-7. Boucle for du style du langage C

La boucle for dite de style C (c'est-à-dire du style du langage C) nécessite la gestion des conditions d'itération :

 
Sélectionnez
for (my $i = 0; $i <= 10; $i += 2)
{
    say "$i * $i = ", $i * $i;
}

Vous devez déclarer explicitement une variable d'itération dans la construction de boucle, car cette boucle n'effectue ni aliasing ni assignation à la variable par défaut. Bien que toute variable déclarée dans la construction de boucle ait une portée limitée au bloc de celle-ci, Perl ne limitera pas la portée lexicale d'une variable déclarée à l'extérieur de la boucle :

 
Sélectionnez
my $i = 'cochon';

for ($i = 0; $i <= 10; $i += 2)
{
    say "$i * $i = ", $i * $i;
}

isnt( $i, 'cochon', 'Contenu de $i remplacé par un nombre' );

La construction de la boucle peut avoir trois sous-expressions. La première — la section d'initialisation — s'exécute une seule fois, avant l'exécution du corps de la boucle. Perl évalue la deuxième sous-expression — la comparaison conditionnelle — avant chaque itération du corps de la boucle. Lorsque l'évaluation conduit à une valeur vraie, l'itération se produit. Quand l'évaluation retourne une valeur fausse, l'itération s'arrête. La dernière sous-expression s'exécute après chaque itération.

 
Sélectionnez
for (
    # sous-expression d'initialisation de la boucle
    say 'Initialiser', my $i = 0;

    # sous-expression de comparaison conditionnelle
    say "Itération: $i" and $i < 10;

    # sous-expression de fin d'itération
    say 'Incrémenter ' . $i++
)
{
    say "$i * $i = ", $i * $i;
}

Remarquez l'absence d'un point-virgule après la sous-expression finale ainsi que l'utilisation de l'opérateur virgule et le and à faible priorité ; cette syntaxe est étonnamment capricieuse. Lorsque c'est possible, préférez le style de la boucle foreach à la boucle for de style C.

Les trois sous-expressions sont facultatives. Une boucle for infinie est :

 
Sélectionnez
for (;;) { ... }

IV-D-8. While et until

Une boucle while continue jusqu'à ce que l'expression conditionnelle de boucle soit évaluée à une valeur booléenne fausse. Une boucle infinie idiomatique est :

 
Sélectionnez
while (1) { ... }

Contrairement au style d'itération de la boucle foreach, la condition de la boucle while n'a aucun effet secondaire. Si @values a un ou plusieurs éléments, ce code est également une boucle infinie, parce que chaque itération évaluera @values dans un contexte scalaire à une valeur différente de 0 et l'itération continuera :

 
Sélectionnez
while (@values)
{
    say $values[0];
}

Pour éviter une telle boucle while infinie, utilisez une mise à jour destructive du tableau @values en modifiant le tableau lors de chaque itération :

 
Sélectionnez
while (@values)
{
    my $value = shift @values;
    say $value;
}

La modification de @values à l'intérieur du test de la condition de while fonctionne aussi, mais il y a quelques subtilités liées à la valeur vraie ou fausse de chaque valeur.

 
Sélectionnez
while (my $value = shift @values)
{
    say $value;
}

Cette boucle s'arrête dès qu'elle atteint un élément évalué à faux, pas nécessairement quand elle a épuisé le tableau. C'est peut-être le comportement souhaité, mais il mérite probablement un commentaire pour expliquer pourquoi.

La boucle until inverse le sens du test de la boucle while. L'itération se poursuit tant que l'expression conditionnelle de boucle correspond à une valeur fausse :

 
Sélectionnez
until ($fin_execution)
{
    ...
}

L'utilisation canonique de la boucle while est d'itérer sur des données en entrée provenant d'un descripteur de fichier :

 
Sélectionnez
while (<$fh>)
{
    # supprimer les sauts de ligne
    chomp;
    ...
}

Perl interprète cette boucle while comme si vous aviez écrit :

 
Sélectionnez
while (defined($_ = <$fh>))
{
    # supprimer les sauts de ligne
    chomp;
    ...
}

Sans le defined implicite, toute ligne lue à partir du descripteur de fichier qui aurait été évaluée à une valeur fausse dans un contexte scalaire — par exemple une ligne vide, ou qui ne contiendrait que le caractère 0 — mettrait fin à la boucle. L'opérateur readline (<>) retourne une valeur indéfinie seulement quand il a atteint la fin du fichier.

Les deux while et until ont des formes postfixées, comme la boucle infinie 1 while 1;. Toute expression simple est appropriée pour une boucle postfixée while ou until, y compris l'exemple classique « Hello world ! » des ordinateurs 8 bits du début des années 1980 :

 
Sélectionnez
print "Hello, world!  " while 1; # boucle infinie

Les boucles infinies sont plus utiles qu'il n'y paraît, en particulier pour les boucles de programmation événementielle dans les programmes d'interface graphique, les interpréteurs de programmes ou les serveurs de réseau :

 
Sélectionnez
$server->dispatch_results until $should_shutdown;

Un bloc do est analysé comme une expression unique qui peut contenir plusieurs expressions. Contrairement à la forme de bloc de la boucle while, le bloc do avec un while ou un until postfixé exécutera son corps au moins une fois. Cette construction est moins fréquente que les autres boucles, mais non moins puissante.

IV-D-9. Boucles imbriquées

Vous pouvez imbriquer des boucles dans d'autres boucles :

 
Sélectionnez
for my $couleur ("Pique", "Coeur", "Carreau", "Trefle")
{
    for my $valeurs (@valeur_cartes) { ... }
}

Lorsque vous procédez ainsi, déclarez vos variables d'itération ! Le risque de confusion avec la variable par défaut et sa portée est trop important dans le cas contraire.

Assez souvent, les débutants consomment accidentellement tout le contenu associé à des descripteurs de fichiers en imbriquant des boucles foreach et while :

 
Sélectionnez
use autodie 'open';
open my $fh, '<', $some_file;

for my $prefix (@prefixes)
{
    # NE PAS UTILISER; code bogué
    while (<$fh>)
    {
        say $prefix, $_;
        }
}

L'ouverture du descripteur de fichier en dehors de la boucle for laisse la position du fichier inchangée entre les itérations de la boucle. À la deuxième itération de la boucle for, la boucle while n'aura plus rien à lire et s'arrêtera. Vous pouvez résoudre ce problème de plusieurs façons ; rouvrir le fichier à chaque itération de la boucle for (coûteux, mais simple), charger la totalité du fichier dans la mémoire (ce qui fonctionne très bien avec de petits fichiers), ou utiliser l'opérateur seek pour ramener le descripteur de fichier au début du fichier à chaque itération de la boucle for :

 
Sélectionnez
for my $prefix (@prefixes)
{
    while (<$fh>)
    {
        say $prefix, $_;
    }

    seek $fh, 0, 0;
}

IV-D-10. Contrôle de boucle

Parfois, vous avez besoin de sortir d'une boucle avant d'avoir épuisé les conditions d'itération. Les mécanismes standard de contrôle de Perl — les exceptions et l'opérateur return de fin de fonction — marchent, mais vous pouvez également utiliser les instructions de contrôle de boucle.

L'instruction next redémarre la boucle à l'itération suivante. Utilisez-la quand vous avez fait le nécessaire lors de l'itération courante. Par exemple, pour boucler sur les lignes d'un fichier et sauter tout ce qui commence par le caractère de commentaire # :

 
Sélectionnez
while (<$fh>)
{
    next if /\A#/;
    ...
}

Exit multiples versus if imbriquées

Comparez l'utilisation de next à l'option suivante : englobez le reste du corps du bloc dans un if. Maintenant, examinez ce qui se passe si vous avez plusieurs conditions qui pourraient causer un saut de ligne. Les modificateurs de contrôle de la boucle avec les instructions conditionnelles postfixées peuvent rendre votre code beaucoup plus lisible.

L'instruction last arrête la boucle immédiatement. Pour terminer le traitement d'un fichier une fois que vous avez détecté le symbole de fin, écrivez :

 
Sélectionnez
while (<$fh>)
{
    next if /\A#/;
    last if /\A__END__/
    ...
}

L'instruction redo redémarre l'itération courante sans réévaluer la condition. Cela peut être utile dans les rares cas où vous souhaitez modifier la ligne que vous avez lue, puis recommencez à traiter depuis le début sans l'écraser avec une autre ligne. Pour implémenter un analyseur de fichier simpliste qui fusionne les lignes se terminant par un antislash :

 
Sélectionnez
while (my $line = <$fh>)
{
    chomp $line;

    # teste la présence de l'antislash en fin de ligne
    if ($line =~ s{\\$}{})
    {
        $line .= <$fh>;
        chomp $line;
        redo;
    }

    ...
}

L'utilisation des instructions de contrôle de boucle dans les boucles imbriquées peut être déroutante. Si vous ne pouvez pas éviter les boucles imbriquées — en reportant par exemple le code des boucles internes dans des fonctions, utilisez une étiquette de boucle pour plus de clarté :

 
Sélectionnez
LINE:
while (<$fh>)
{
    chomp;

    PREFIX:
    for my $prefix (@prefixes)
    {
        next LINE unless $prefix;
        say "$prefix: $_";
        # le prochain PREFIX est implicite ici
    }
}

IV-D-11. Continue

La construction continue se comporte comme la troisième sous-expression d'une boucle for ; Perl exécute tout bloc continue avant toute itération ultérieure d'une boucle, qu'elle soit due à la répétition normale en boucle ou une réitération prématurée après next. L'équivalent en Perl de l'instruction continue du langage C est next. Vous pouvez l'utiliser avec des boucles while, until, when, ou for. Les exemples utilisant l'opérateur continue sont rares, mais c'est utile chaque fois que vous voulez vous assurer que quelque chose se produit à chaque itération de la boucle, quelle que soit la façon dont cette itération se termine :

 
Sélectionnez
while ($i < 10 )
{
    next unless $i % 2;
    say $i;
}
continue
{
    say 'Continuing...';
    $i++;
}

Sachez cependant qu'un bloc continue ne s'exécute pas lorsque le flux de contrôle quitte une boucle en raison d'une instruction last ou redo.

IV-D-12. Instructions switch

Perl 5.10 a introduit une nouvelle construction nommée given comme une instruction switch dans l'esprit de Perl. Celle-ci était peut-être trop riche en fonctionnalités et n'a en définitive pas vraiment bien fonctionné ; given est encore considéré comme une construction expérimentale, même si elle est moins boguée dans 5.18 que dans n'importe quelle version précédente de Perl. C'est une belle façon de dire « ne l'utilisez pas, sauf si vous savez vraiment ce que vous faites ».

Si vous avez besoin d'une instruction switch, utilisez for pour créer l'alias de la variable par défaut ($_) et when pour le comparer à des expressions simples ayant la sémantique d'une reconnaissance intelligente (Smart MatchingReconnaissances intelligentes). Pour écrire le jeu Caillou / papier / ciseaux  :

 
Sélectionnez
my @options  = ( \&caillou, \&papier, \&ciseaux );
my $confus = "Je n'ai pas compris ton coup.";

do
{
    say "Caillou, papier, ciseaux. Ton choix : ";
    chomp( my $user = <STDIN> );
    my $computer_match = $options[ rand @options ];
    $computer_match->( lc( $user ) );
} until (eof);

sub caillou
{
    print "J'ai choisi caillou.  ";

    for (shift)
    {
        when (/papier/)  { say 'Tu as gagné!' };
        when (/caillou/) { say 'Égalité !'  };
        when (/ciseaux/) { say 'J\'ai gagné'   };
        default          { say $confus  };
    }
}

sub papier
{
    print "J'ai choisi papier.  ";

    for (shift)
    {
        when (/papier/)  { say 'Égalité !'  };
        when (/caillou/) { say 'J\'ai gagné !'   };
        when (/ciseaux)  { say 'Tu as gagné !' };
        default          { say $confus  };
    }
}

sub ciseaux
{
    print "J'ai choisi ciseaux.  ";

    for (shift)
    {
        when (/papier/)  { say 'J\'ai gagné !'   };
        when (/caillou/) { say 'Tu as gagné !' };
        when (/ciseaux/) { say 'Égalité !'  };
        default          { say $confus  };
    }
}

Perl exécute la règle default quand aucune des autres conditions ne correspond.

Distribution simplifiée avec les MultiMethods

Le module CPAN MooseX::MultiMethods fournit une autre technique pour simplifier ce code.

IV-D-13. Appels de fonctions terminaux et récursion

Un appel de fonction terminal (tailcall) se produit lorsque la dernière expression dans une fonction est un appel à une autre fonction. La valeur de retour de la fonction externe devient la valeur de retour de la fonction interne :

 
Sélectionnez
sub log_and_greet_person
{
    my $name = shift;
    log( "Greeting $name" );

    return greet_person( $name );
}

Retourner de greet_person() directement à l'appelant de log_and_greet_person() est plus efficace que retourner à log_and_greet_person(), puis de log_and_greet_person(). Le retour direct de greet_person() à l'appelant de log_and_greet_person() est une optimisation par appel terminal.

Un programme fortement récursif (RécursivitéRécursivité) —surtout les programmes mutuellement récursifs — peut consommer beaucoup de mémoire. Les optimisations d'appels terminaux réduisent la mémoire nécessaire pour la comptabilité interne du flux de contrôle et peuvent rendre moins chers les algorithmes coûteux. Malheureusement, le compilateur Perl n'effectue pas automatiquement cette optimisation, donc vous devez la faire vous-même quand c'est nécessaire.

L'une des formes d'appel de l'opérateur interne goto appelle une fonction comme si la fonction en cours n'était jamais appelée, essentiellement en effaçant la conservation en mémoire du nouvel appel de fonction. La syntaxe un peu laide perturbe les personnes à qui l'on a dit et répété de « ne jamais utiliser de goto », mais cela fonctionne :

 
Sélectionnez
sub log_and_greet_person
{
    my ($name) = @_;
    log( "Greeting $name" );

    goto &greet_person;
}

Cet exemple présente deux caractéristiques importantes. Tout d'abord, goto &nom_de_la_fonction ou goto &$reference_de_la_fonction nécessite l'utilisation du sigil de la fonction (&) afin que l'analyseur sache qu'il doit effectuer un appel terminal au lieu de sauter inconditionnellement à une étiquette. Deuxièmement, cette forme d'appel de fonction passe implicitement le contenu de @_ à la fonction appelée. Vous pouvez modifier @_ pour modifier les arguments passés si vous le désirez.

Cette technique est relativement rare ; elle est utile surtout lorsque vous voulez détourner le flot d'exécution pour ne pas gêner d'autres fonctions inspectant caller (par exemple lorsque vous implémentez un log spécial ou une sorte de fonctionnalité de débogage), ou lorsque vous utilisez un algorithme qui exige beaucoup de récursivité. Souvenez-vous d'elle si vous en avez besoin, mais vous pouvez aussi ne pas l'utiliser.

IV-E. Scalaires

Le type de données fondamental de Perl est le scalaire : une valeur unique, discrète. Cette valeur peut être une chaîne de caractères, un entier, une valeur à virgule flottante, un descripteur de fichier ou une référence, mais c'est toujours une valeur unique. Les scalaires peuvent être des variables lexicales, de paquetage ou globales (Variables globalesVariables globales). Vous pouvez déclarer uniquement des variables lexicales ou de paquetage. Les noms des variables scalaires doivent être conformes aux directives standard de nommage (NomsNoms). Les variables scalaires utilisent toujours comme sigil le signe dollar ($) (Sigils de variablesSigils de variables).

Sigils variants et contexte

Les valeurs scalaires et le contexte scalaire sont intimement liés ; l'assignation à un scalaire impose le contexte scalaire. L'utilisation du sigil scalaire avec une variable composite impose un contexte scalaire pour accéder à un seul élément du hachage ou du tableau.

IV-E-1. Scalaires et types

Une variable scalaire peut contenir n'importe quel type de valeur scalaire sans conversions, coercitions, ou conversions de type spéciales. Le type de valeur stocké dans une variable scalaire, une fois affecté, peut changer arbitrairement :

 
Sélectionnez
my $valeur;
$valeur = 123.456;
$valeur = 77;
$valeur = "Je suis le gros orteil de Charlie.";
$valeur = Store::IceCream->new;

Même si ce code est légal, le changement du type de données stocké dans un scalaire est source de confusion.

Cette souplesse de type conduit souvent à la contrainte de la valeur (CoercitionCoercition). Par exemple, vous pouvez traiter le contenu d'un scalaire comme une chaîne, même si vous ne lui avez pas affecté explicitement une chaîne :

 
Sélectionnez
my $code_postal     = 97123;
my $ville_etat_code = 'Hillsboro, Oregon' . ' ' . $code_postal;

Vous pouvez également utiliser des opérations mathématiques sur les chaînes :

 
Sélectionnez
my $call_sign = 'KBMIU';

# met à jour la valeur courante et retourne la nouvelle valeur
my $next_sign = ++$call_sign;  # $next_sign et $call_sign valent KBMIV

# retourne l'ancienne valeur, puis met à jour la valeur
my $curr_sign = $call_sign++;

# mais ceci ne fonctionne pas :
my $new_sign  = $call_sign + 1;

La magie de l'incrémentation unidirectionnelle

Ce comportement magique d'incrémentation de chaîne n'a pas un comportement magique de décrémentation correspondant. Vous ne pouvez pas restaurer la valeur précédente de la chaîne en écrivant $call_sign--.

Cette opération d'incrémentation de chaîne transforme a en b et z en aa, en respectant le jeu de caractères et la casse. Alors que ZZ9 devient AAA0, ZZ09 devient ZZ10 — les nombres s'y ajoutent tant qu'il y a des places plus signifiantes à augmenter, comme sur un compteur kilométrique de véhicule.

L'évaluation d'une référence (RéférencesRéférences) en contexte de chaîne produit une chaîne. L'évaluation d'une référence en contexte numérique produit un nombre. Aucune des opérations ne modifie la référence en place, mais vous ne pouvez pas recréer la référence à partir d'un des résultats :

 
Sélectionnez
my $auteurs     = [qw( Pratchett Vinge Conway )];
my $stringy_ref = '' . $auteurs;
my $numeric_ref =  0 + $auteurs;

$auteurs est toujours utilisable comme référence, mais $stringy_ref est une chaîne sans aucun rapport avec la référence et $numeric_ref est un nombre sans aucun rapport avec la référence.

Pour permettre la coercition sans perte de données, les scalaires Perl peuvent contenir à la fois des composantes numériques et de chaîne. La structure de données interne qui représente un scalaire en Perl a un emplacement numérique et un emplacement de chaîne. L'accès à une chaîne dans un contexte numérique produit un scalaire avec les deux valeurs, de chaîne et numérique. La fonction dualvar() du module standard Scalar::Util vous permet de manipuler les deux valeurs directement dans un seul scalaire.

Les scalaires ne contiennent pas d'emplacement séparé pour les valeurs booléennes. Dans le contexte booléen, la chaîne vide ('') et la chaîne nulle '0' sont évaluées à des valeurs fausses. Toutes les autres chaînes sont évaluées à des valeurs vraies. Dans un contexte booléen, les nombres évalués à zéro (0, 0.0, et 0e0) sont évalués à faux. Tous les autres nombres sont évalués à vrai.

Qu'est-ce que la vérité ?

Attention, les chaînes '0.0' et '0e0' sont évaluées à des valeurs vraies. C'est un endroit où Perl fait la distinction entre ce qui ressemble à un nombre et ce qui est vraiment un nombre.

Une autre valeur est toujours évaluée à faux : undef.

IV-F. Tableaux

En Perl, les tableaux sont des structures de données de première classe — le langage les prend en charge comme un type de données intégré — qui stockent zéro, un ou plusieurs scalaires. Vous pouvez accéder aux membres individuels du tableau par des indices entiers et vous pouvez ajouter ou supprimer des éléments à volonté. Les tableaux grandissent ou rétrécissent selon vos besoins.

Le sigil @ désigne un tableau. Pour déclarer un tableau :

 
Sélectionnez
my @items;

IV-F-1. Éléments de tableau

Utilisez le sigil scalaire pour accéder à un élément individuel d'un tableau. $chats[0] est une utilisation sans ambiguïté du tableau @chats, parce que les crochets postfixés (FixitéFixité) ([]) signifient toujours l'accès à un tableau par un indice.

Le premier élément d'un tableau se trouve à l'indice zéro :

 
Sélectionnez
# @chats contient une liste d'objets Chat
my $premier_chat = $chats[0];

Le dernier indice d'un tableau dépend du nombre d'éléments dans le tableau. Un tableau évalué dans un contexte scalaire (en raison d'affectation scalaire, concaténation de chaînes, addition ou contexte booléen) retourne le nombre d'éléments du tableau :

 
Sélectionnez
# affectation scalaire
my $num_chats = @chats;

# concaténation 
say 'J\'ai ' . @chats . ' chats!';

# addition
my $num_animaux = @chats + @chiens + @poissons;

# contexte booléen
say 'Oui, un propriétaire de chat(s) !' if @chats;

Pour obtenir l'indice du dernier élément d'un tableau, soustrayez un du nombre des éléments du tableau (parce que les indices de tableau commencent à 0) ou utilisez la syntaxe un peu lourde $#chats :

 
Sélectionnez
my $premier_indice = 0;
my $dernier_indice  = @chats - 1;
# ou
# my $dernier_indice = $#chats;

say   "Mon premier chat est à l'indice $premier_indice, "
    . "et mon dernier chat est à l'indice $dernier_indice."

Lorsque vous vous intéressez plus à la position relative d'un élément dans le tableau, utilisez un indice de tableau négatif. Le dernier élément d'un tableau est disponible à l'indice -1. L'avant-dernier élément du tableau est disponible à l'indice -2, et ainsi de suite :

 
Sélectionnez
my $dernier_chat       = $chats[-1];
my $avant_dernier_chat = $chats[-2];

$# a une autre utilisation : il redimensionne un tableau en affectant une valeur à $#array. Souvenez-vous qu'en Perl les tableaux sont modifiables. Ils se dilatent ou se contractent selon les besoins. Lorsque vous réduisez un tableau, Perl va éliminer les valeurs qui ne rentrent plus dans le tableau redimensionné. Lorsque vous agrandissez un tableau, Perl remplira les positions supplémentaires par des undef.

IV-F-2. Initialisation des tableaux

Assignez des valeurs aux positions individuelles d'un tableau directement par indice :

 
Sélectionnez
my @chats;
$chats[3] = 'Jack';
$chats[2] = 'Tuxedo';
$chats[0] = 'Daisy';
$chats[1] = 'Petunia';
$chats[4] = 'Brad';
$chats[5] = 'Choco';

Si vous affectez une valeur à un indice au-delà de la taille courante, Perl va étendre le tableau pour tenir compte de la nouvelle taille et remplira toutes les positions intermédiaires par undef. Après la première affectation ci-dessus, le tableau contiendra undef aux positions 0, 1, et 2 et Jack à la position 3.

Un raccourci d'affectation consiste à initialiser un tableau à partir d'une liste :

 
Sélectionnez
my @chats = ( 'Daisy', 'Petunia', 'Tuxedo', ... );

… mais rappelez-vous que ces parenthèses ne créent pas une liste. Sans parenthèses, le chat Daisy serait attribué comme le premier et le seul élément du tableau, en raison de la précédence des opérateurs (PrioritéOrdre des opérations (priorité)). Petunia, Tuxedo et tous les autres chats seraient évalués dans un contexte vide et Perl se plaindra. Il en serait de même pour les chats, Petunia en particulier...

Vous pouvez assigner à un tableau toute expression qui produit une liste dans un contexte de liste :

 
Sélectionnez
my @chats    = get_liste_chats();
my @timeinfo = localtime();
my @nums     = 1 .. 10;

L'initialisation d'un élément scalaire du tableau impose un contexte scalaire, tandis que l'initialisation du tableau dans son ensemble impose le contexte de liste.

Pour supprimer les éléments d'un tableau, assignez-lui une liste vide :

 
Sélectionnez
my @dates = ( 1969, 2001, 2010, 2051, 1787 );
...
@dates    = ();

C'est l'un des seuls cas où les parenthèses indiquent une liste ; sans quelque chose pour indiquer une liste, Perl et les lecteurs du code seraient confus.

Les tableaux commencent par être vides

my @items = (); est une version plus longue et bruyante de my @items;. Les tableaux fraîchement déclarés commencent par être vides.

IV-F-3. Opérations sur tableaux

Parfois, un tableau est plus pratique comme collection ordonnée et mutable d'éléments que comme une correspondance entre des indices et des valeurs. Perl fournit plusieurs opérations pour manipuler des éléments de tableau sans l'aide des indices.

Les opérateurs push et pop ajoutent et suppriment un ou plusieurs éléments à la fin d'un tableau, respectivement :

 
Sélectionnez
my @repas;

# qu'est-ce qu'il y a à manger?
push @repas, qw( hamburgers pizza lasagne navet );

# ... mais votre neveu déteste les légumes
pop @repas;

Vous pouvez push une liste de valeurs vers un tableau, mais vous ne pouvez pop qu'un seul élément à la fois. push retourne le nouveau nombre d'éléments dans le tableau. pop retourne l'élément supprimé.

Parce que push fonctionne sur une liste, vous pouvez facilement fusionner les éléments de plusieurs tableaux par un :

 
Sélectionnez
push @repas, @petit_dejeuner, @dejeuner, @diner;

De même, unshift et shift ajoutent ou suppriment un élément au début d'un tableau, respectivement :

 
Sélectionnez
# élargir nos horizons culinaires
unshift @repas, qw( tofu spanakopita taquitos );

# changer d'idée
shift @repas;

unshift insère une liste d'éléments au début du tableau et retourne le nouveau nombre d'éléments dans le tableau. shift supprime et retourne le premier élément du tableau.

Peu de programmes utilisent les valeurs de retour de push et unshift.

L'opérateur splice supprime et remplace les éléments d'un tableau à partir d'un décalage, une longueur de tranche de liste et des éléments de remplacement donnés. Tant le remplacement que la suppression sont facultatifs ; vous pouvez omettre l'un des comportements. La description perlfunc de splice montre ses équivalences avec push, pop, shift et unshift. Une utilisation efficace est la suppression de deux éléments d'un tableau :

 
Sélectionnez
my ($gagnant, $finaliste) = splice @finalistes, 0, 2;

# ou
my $gagnant               = shift @finalistes;
my $finaliste             = shift @finalistes;

À partir de Perl 5.12, l'opérateur each vous permet de parcourir un tableau par indice et valeur :

 
Sélectionnez
while (my ($index, $valeur) = each @livres)
{
    say "#$index: $valeur";
    ...
}

IV-F-4. Tranches de tableaux

Une tranche de tableau vous permet d'accéder aux éléments d'un tableau dans un contexte de liste. Contrairement à l'accès scalaire à un élément du tableau, cette opération d'indexation prend une liste de zéro ou plusieurs indices et utilise le sigil de tableau (@) :

 
Sélectionnez
my @jeunes_chats   = @chats[-1, -2];
my @vieux_chats    = @chats[0 .. 2];
my @selected_chats = @chats[ @indexes ];

Les tranches de tableau sont utiles pour effectuer des affectations :

 
Sélectionnez
@users[ @replace_indices ] = @replace_users;

La seule différence syntaxique entre une tranche de tableau d'un élément et l'accès scalaire à un élément du tableau est le sigil. La différence sémantique est plus importante : une tranche de tableau impose toujours un contexte de liste. Une tranche de tableau évaluée dans un contexte scalaire produira un avertissement :

Scalar value @chats[1] better written as $chats[1]...

Une tranche de tableau impose un contexte de liste sur l'expression utilisée comme indice :

 
Sélectionnez
# fonction appelée dans un contexte de liste
my @beaux_chats = @chats[ get_chat_indices() ];

Une tranche peut contenir zéro ou plusieurs éléments, y compris un seul :

 
Sélectionnez
# tranche de tableau à un seul élément; contexte de liste 
@chats[-1] = get_plus_de_chats();

# accès au tableau à un seul élément; contexte scalaire 
$chats[-1] = get_plus_de_chats();

IV-F-5. Tableaux et contexte

Dans le contexte de liste, les tableaux sont aplatis en listes. Si vous passez plusieurs tableaux à une fonction normale, ils seront aplatis en une seule liste :

 
Sélectionnez
my @chats  = qw( Daisy Petunia Tuxedo Brad Jack Choco );
my @chiens = qw( Rodney Lucky Rosie );

emmener_chez_le_veto( @chats, @chiens );

sub emmener_chez_le_veto
{
    # BOGUE: ne pas utiliser !
    my (@chats, @chiens) = @_;
    ...
}

À l'intérieur de la fonction emmener_chez_le_veto, @_ contiendra neuf éléments et non pas deux, parce que l'affectation des listes aux tableaux est gourmande. Un tableau consommera autant d'éléments de la liste que possible. Après l'affectation, @chats contiendra tous les arguments passés à la fonction. @chiens sera vide... mais Rosie pense qu'elle est une chatte, c'est pas si grave que ça...

Ce comportement d'aplatissement déroute parfois les débutants qui tentent de créer des tableaux imbriqués :

 
Sélectionnez
# crée un tableau simple, pas un tableau de tableaux
my @nombres = (1 .. 10, (11 .. 20, (21 .. 30)));

... mais ces expressions sont de fait équivalentes à :

 
Sélectionnez
# les parenthèses ne créent pas des listes
my @nombres = ( 1 .. 10, 11 .. 20, 21 .. 30 );

# crée un tableau simple, pas un tableau de tableaux
my @nombres = 1 .. 30;

... parce que les parenthèses ne font que grouper les expressions. Elles ne créent pas des listes. Pour éviter ce comportement d'aplatissement, utilisez les références des tableaux (Références de tableauxRéférences de tableaux).

IV-F-6. Interpolation de tableau

Les tableaux sont interpolés en chaînes comme listes de chaînes issues de chaque élément séparées par la valeur courante de la variable magique globale $". La valeur par défaut de cette variable est un espace unique. Sa mnémonique dans le module English.pm est $LIST_SEPARATOR. Donc :

 
Sélectionnez
my @alphabet = 'a' .. 'z';
say "[@alphabet]";
# imprime : [a b c d e f g h i j k l m n o p q r s t u v w x y z]

Vous pouvez « localiser » (donner une valeur locale) au séparateur de liste $" pour faciliter votre débogage. Le mérite pour cette technique revient à Mark Jason Dominus :

 
Sélectionnez
# quoi de neuf dans ce tableau ?
local $" = ')(';
say "(@friandises)";
(tarte)(gâteau)(beignets)(biscuits)(brioche à la cannelle)

IV-G. Tables de hachage

Une table de hachage ou plus simplement un hachage est une structure de données de première classe de Perl qui associe des clés littérales à des valeurs scalaires. Tout comme le nom d'une variable correspond à quelque chose qui contient une valeur, une clé de hachage se réfère à quelque chose qui contient une valeur. Pensez à une table de hachage comme à une liste de contacts : vous utilisez les noms de vos amis pour regarder leurs numéros de téléphone. D'autres langages appellent les hachages tables, tableaux associatifs, dictionnaires ou maps.

Les tables de hachage ont deux propriétés importantes : elles stockent un scalaire par clé unique et elles ne fournissent aucun ordre spécifique des clés. Gardez à l'esprit cette dernière propriété. Bien qu'elle ait toujours été vraie en Perl, elle est encore plus vraie à partir de Perl 5.18.

IV-G-1. Déclarer des tables de hachage

Les tables de hachage utilisent le sigil %. Déclarez une table de hachage ainsi :

 
Sélectionnez
my %preferences_parfums;

Une table de hachage commence par être vide. Vous pourriez écrire my %preferences_parfums = ();parfums = ();s = ();, mais c'est redondant.

Les tables de hachage utilisent le sigil scalaire $ pour accéder aux éléments et les accolades { } pour l'accès par clé :

 
Sélectionnez
my %preferences_parfums;
$preferences_parfums{Gabi}    = 'Chocolat noir à la framboise';
$preferences_parfums{Annette} = 'Vanille française';

Assignez une liste de clés et de valeurs à un hachage en une seule expression :

 
Sélectionnez
my %preferences_parfums = (
    'Gabi',    'Chocolat noir à la framboise',
    'Annette', 'Vanille française',
);

Les tables de hachage stockent des paires clé/valeur. Perl vous avertira si vous affectez un nombre impair d'éléments à une table de hachage. L'usage idiomatique utilise souvent l'opérateur « grosse virgule » ou fat comma (=>) pour associer des valeurs aux clés, car il rend l'association plus visible :

 
Sélectionnez
my %preferences_parfums = (
    Gabi    => 'Chocolat noir à la framboise',
    Annette => 'Vanille française',
);

L'opérateur => agit comme la virgule ordinaire et met également automatiquement entre guillemets simples le « mot nu » (bareword) qui le précède (Mots nusMots nus). Le pragma strict n'émettra pas d'avertissement pour un tel bareword — et si vous avez une fonction du même nom que la clé de hachage, l'opérateur => n'appellera pas la fonction :

 
Sélectionnez
sub nom { 'Leonardo' }

my %adresse = (
    nom => '1123 Fib Place'
);

La clé de cette valeur sera nom et pas Leonardo. Pour appeler la fonction, rendez son appel explicite :

 
Sélectionnez
my %adresse = (
    nom() => '1123 Fib Place'
);

Pour vider une table de hachage, assignez-lui une liste vide. Vous pouvez voir parfois undef %hash, mais c'est un peu laid :

 
Sélectionnez
%preferences_parfums = ();

IV-G-2. Indexation des tables de hachage

Pour accéder à une valeur individuelle d'une table de hachage, utilisez une clé (une opération d'accès à base de clé) :

 
Sélectionnez
my $adresse = $adresses{$nom};

Dans cet exemple, $nom contient une chaîne qui est aussi une clé de hachage. Comme dans le cas de l'accès à un élément d'un tableau, le sigil de la table de hachage a été modifié de % en $ pour indiquer l'accès par clé à une valeur scalaire.

Vous pouvez également utiliser des chaînes littérales comme clés de hachage. Perl met automatiquement entre guillemets simples les « mots nus » selon les mêmes règles que celles suivies par l'opérateur => :

 
Sélectionnez
# mise automatique entre guillemets simples
my $adresse = $adresses{Victor};

# doit être entourée par des guillemets; pas un bareword valide
my $adresse = $adresses{'Sue-Linn'};

# l'appel de fonction doit se faire de façon explicite
my $adresse = $adresses{get_nom()};

Ne me citez pas

Les débutants ont souvent la tendance de mettre toujours les clés de hachage littérales entre guillemets simples, mais les développeurs expérimentés évitent cela autant que possible. Si vous codez de cette façon, vous pouvez utiliser la présence rare des guillemets pour indiquer que vous faites quelque chose de différent.

Même les opérateurs internes Perl subissent la mise automatique entre guillemets :

 
Sélectionnez
my %adresses =
(
    Leonardo => '1123 Fib Place',
    Utako    => 'Cantor Hotel, Room 1',
);

sub get_adresse_du_nom
{
    return $adresses{+shift};
}

L'opérateur plus unaire (Coercitions unairesCoercitions unaires) transforme en une expression ce qui serait sinon un bareword (shift) soumis aux règles de mise automatiques entre guillemets. En conséquence, vous pouvez utiliser une expression quelconque — pas seulement un appel de fonction — comme clé de hachage :

 
Sélectionnez
# ne faites pas ceci quand même
my $adresse = $adresses{reverse 'odranoeL'};

# l'interpolation est permise
my $adresse = $adresses{"$prenom $nom"};

# les appels de méthode sont aussi autorisés
my $adresse = $adresses{ $user->nom };

Les clés de hachage ne peuvent être que des chaînes de caractères. Tout ce qui a pour résultat une chaîne de caractères représente une clé de hachage acceptable. Perl ira jusqu'à convertir (CoercitionCoercition) tout autre type en une chaîne. Par exemple, si vous utilisez un objet comme clé de hachage, vous obtiendrez la version transformée en chaîne de caractères de cet objet à la place de l'objet lui-même :

 
Sélectionnez
for my $isbn (@isbns)
{
    my $livre = Livre->chercher_par_isbn( $isbn );

    # peu de chances de faire ce que vous voulez
    $livres{$livre} = $livre->prix;
}

IV-G-3. Existence des clés de hachage

L'opérateur exists renvoie une valeur booléenne pour indiquer si une table de hachage contient la clé donnée :

 
Sélectionnez
my %adresses =
(
    Leonardo => '1123 Fib Place',
    Utako    => 'Cantor Hotel, Room 1',
);

say "J'ai l'adresse de Leonardo"
    if exists $adresses{Leonardo};
say "J'ai l'adresse de Warnie"
    if exists $adresses{Warnie};

Le fait d'utiliser exists au lieu d'accéder directement à la clé de hachage évite deux problèmes. Tout d'abord, on ne vérifie pas la nature booléenne de la valeur correspondante ; une clé de hachage peut exister avec une valeur même si cette valeur est évaluée à un booléen faux (undef y compris) :

 
Sélectionnez
my  %false_key_value = ( 0 => '' );
ok( %false_key_value,
     'hachage contenant clé et valeur à faux 
      doit être évalué à une valeur vrai' );

Deuxièmement, exists évite l'autovivification (AutovivificationAutovivification) au sein des structures de données imbriquées (Structures de données imbriquéesStructures de données imbriquées).

Si une clé de hachage existe, sa valeur peut être undef. Vérifiez cela avec defined :

 
Sélectionnez
$adresses{Leibniz} = undef;

say "Gottfried habite à $adresses{Leibniz}"
    if exists  $adresses{Leibniz}
    && defined $adresses{Leibniz};

IV-G-4. Accéder aux clés et valeurs d'une table de hachage

Les tables de hachage sont des variables agrégées, mais leur nature en paires est unique. Perl vous permet d'itérer les clés d'une table de hachage, ses valeurs ou sur les paires de clés et valeurs. L'opérateur keys produit une liste de clés de hachage :

 
Sélectionnez
for my $destinataire (keys %adresses)
{
    say "Trouvé une adresse pour $destinataire !";
}

L'opérateur values produit une liste de valeurs :

 
Sélectionnez
for my $adresse (values %adresses)
{
    say "Quelqu'un habite à $adresse";
}

L'opérateur each produit une liste de paires clé/valeur :

 
Sélectionnez
while (my ($destinataire, $adresse) = each %adresses)
{
    say "$destinataire habite à $address";
}

Contrairement aux tableaux, il n'y a aucun ordre évident dans ces listes. L'ordre dépend de l'implémentation interne de la table de hachage, de sa taille, de la version de Perl utilisée et d'un facteur aléatoire. Malgré cela, l'ordre des éléments est cohérent entre keys, values et each. La modification de la table de hachage peut modifier l'ordre, mais vous pouvez compter sur cet ordre si la table reste inchangée. Cependant, même si deux tables de hachage ont les mêmes clés et les mêmes valeurs, vous ne pouvez pas compter sur le même ordre d'itération des éléments des deux tables. Elles peuvent avoir été construites différemment ou on en a supprimé des éléments. À partir de Perl 5.18, même si elles ont été construites de la même façon, vous ne pouvez pas compter sur le même ordre d'itération des deux.

Chaque table de hachage a un seul itérateur pour l'opérateur each. Vous ne pouvez pas parcourir une table de manière fiable plus d'une fois avec each ; si vous commencez une nouvelle itération tandis qu'une autre est en cours, l'ancienne se terminera prématurément et la suivante commencera à mi-chemin. Au cours d'une itération, évitez d'appeler une fonction qui pourrait essayer à son tour de parcourir la table avec each.

En pratique, cela se produit rarement. Réinitialisez l'itérateur avec keys ou values dans un contexte vide lorsque vous en avez besoin :

 
Sélectionnez
# réinitialiser l'itérateur
keys %adresses;

while (my ($destinataire, $adresse) = each %adresses)
{
    ...
}

IV-G-5. Tranches de hachage

Une tranche de table de hachage est une liste de clés ou de valeurs d'un hachage indexée en une seule opération. Pour initialiser plusieurs éléments d'une table à la fois :

 
Sélectionnez
# %chats contient déjà des éléments
@chats{qw( Jack Brad Mars Grumpy )} = (1) x 4;

Ceci équivaut à l'initialisation :

 
Sélectionnez
my %chats = map { $_ => 1 }
            qw( Jack Brad Mars Grumpy );

... sauf que l'initialisation de la tranche ne remplace pas le contenu existant dans la table de hachage.

Les tranches de hachage permettent aussi de récupérer plusieurs valeurs d'un hachage en une seule opération. Comme pour les tranches de tableau, le sigil de la table de hachage est modifié en @ pour indiquer un contexte de liste. L'utilisation des accolades indique l'accès par clé et montre sans ambiguïté le fait que vous travaillez avec une table de hachage :

 
Sélectionnez
my @client_adresses = @adresses{ @clients };

Les tranches rendent facile la fusion de deux tables de hachage :

 
Sélectionnez
my %adresses        = ( ... );
my %canada_adresses = ( ... );

@adresses{ keys   %canada_adresses }
         = values %canada_adresses;

Cela équivaut à une boucle effectuée manuellement sur le contenu de %canada_adresses, mais c'est beaucoup plus court. Notez que ceci repose sur l'ordre d'itération de la table de hachage en restant cohérent entre keys et values. Perl le garantit, mais seulement parce que ces opérations ont lieu sur le même hachage et parce que rien ne modifie celui-ci entre les opérations keys et values.

Que se passe-t-il si la même clé apparaît dans les deux tables de hachage ? L'approche de tranche remplace toujours les paires clé/valeur existantes dans %adresses. Si vous voulez obtenir un autre comportement, une boucle est plus appropriée.

IV-G-6. La table de hachage vide

Une table de hachage vide ne contient aucune clé ou valeur. Elle est évaluée à la valeur fausse dans un contexte booléen. Une table qui contient au moins une paire clé/valeur est évaluée à la valeur vraie dans un contexte booléen, même si toutes les clés ou toutes les valeurs ou les deux étaient elles-mêmes évaluées à des valeurs booléennes faux.

 
Sélectionnez
use Test::More;

my %vide;
ok( ! %vide, 'un hachage vide  est  faux' );

my %false_key = ( 0 => 'valeur vraie' );
ok( %false_key, 'un hachage contenant une clé fausse doit 
                 être considéré comme vrai' );

my %false_value = ( 'clé vraie' => 0 );
ok( %false_value, 'un hachage contenant une valeur fausse doit 
                   être considéré comme vrai' );

done_testing();

En contexte scalaire, une table de hachage renvoie une chaîne qui représente la proportion d'emplacements (ou alvéoles) occupés dans le tableau sous-jacent réalisant le stockage interne des données du hachage, bref un détail d'implémentation que vous pouvez parfaitement ignorer sans le moindre risque. (Dans un contexte scalaire booléen, cette proportion est évaluée à une valeur fausse, alors rappelez-vous plutôt cela à la place des détails sur cette proportion d'emplacements utilisés.)

Dans le contexte de liste, un hachage est évalué à une liste de paires clé/valeur similaire à la liste produite par l'opérateur each. Cependant, vous ne pouvez pas parcourir cette liste de la même manière que la liste produite par each. Cette boucle ne s'arrêtera jamais :

 
Sélectionnez
# boucle infinie dans le cas de hachages non vides
while (my ($key, $value) = %hash)
{
    ...
}

Vous pouvez parcourir la liste des clés et des valeurs avec une boucle for, mais la variable d'itération obtiendra une clé sur une itération et sa valeur sur la suivante, parce que Perl va aplatir la table de hachage en une seule liste de clés et de valeurs entrelacées.

IV-G-7. Idiomes de tables de hachage

Chaque clé n'existe qu'une seule fois dans une table de hachage ; il en résulte que si vous attribuez plusieurs fois des valeurs à la même clé, seule la valeur la plus récente associée à cette clé sera stockée. Ce comportement présente des avantages ! Par exemple, pour trouver des éléments uniques d'une liste :

 
Sélectionnez
my %unique;
undef @unique{ @elements };
my @uniques = keys %unique;

L'utilisation de undef avec une tranche de table de hachage définit les valeurs de celle-ci à undef. Cet idiome est le moyen le moins coûteux de réaliser des opérations ensemblistessur une table de hachage.

Les tables de hachage sont également utiles pour compter des éléments, tels que les adresses IP dans un fichier log :

 
Sélectionnez
my %ip_adresses;

while (my $ligne = <$logfile>)
{
    chomp $ligne;
    my ($ip, $ressource) = analyser_ligne( $ligne );
    $ip_adresses{$ip}++;
    ...
}

La valeur initiale d'une valeur de hachage est undef. L'opérateur de postincrémentation (++) la considère comme zéro. Cette modification de la valeur incrémente une valeur existante pour cette clé. Si aucune valeur n'existe pour cette clé, Perl crée une valeur (undef) et l'incrémente immédiatement à un, car la conversion en un nombre de undef produit la valeur initiale 0.

Cette stratégie fournit un mécanisme de cache utile pour stocker à peu de frais le résultat d'une opération coûteuse  :

 
Sélectionnez
{
    my %user_cache;

    sub fetch_user
    {
        my $id = shift;
        $user_cache{$id} //= create_user($id);
        return $user_cache{$id};
    }
}

Cette opération nommée orcish maneuver (jeu de mot intraduisible pour « Or-cache ») retourne la valeur du hachage, si elle existe. Dans le cas contraire, elle calcule, met en cache et retourne la valeur. L'opérateur d'affectation defined-or (//=) évalue son opérande de gauche. Si cet opérande est n'est pas défini, l'opérateur lui attribue la valeur de son opérande de droite. Autrement dit, s'il n'y a pas de valeur dans la table de hachage pour la clé donnée, cette fonction appellera create_user() avec la clé et mettra à jour la table de hachage.

Perl 5.10 a introduit les opérateurs defined-or d'affectation. Avant la version 5.10, on utilisait l'opérateur d'affectation boolean-or (||=) dans ce but. Malheureusement, certaines valeurs valides sont évaluées à faux en contexte booléen, alors que déterminer si une valeur est définie est presque toujours une meilleure solution. Cette manœuvre paresseuse (5) teste si la valeur mise en cache est définie et pas si sa valeur booléenne est vraie. Vous pouvez encore trouver du code avec le comportement d'avant la version 5.10. Dans ce cas, examinez si l'opérateur defined-or est plus logique.

Si votre fonction prend plusieurs arguments, utilisez une table de hachage pour rassembler des paires clé/valeur dans une seule table comme argument de la fonction nommée :

 
Sélectionnez
sub preparer_coupe_glace
{
    my %parametres = @_;
    ...
}

preparer_coupe_glace( parfum    => 'Citron',
                      garniture => 'morceaux de biscuit' );

Cette approche vous permet de définir des valeurs par défaut :

 
Sélectionnez
sub preparer_coupe_glace
{
    my %parametres             = @_;
    $parametres{parfum}      //= 'Vanille';
    $parametres{garniture}   //= 'caramel';
    $parametres{saupoudrage} //= 100;
    ...
}

... ou de les inclure dans l'initialisation de la table de hachage, comme les dernières affectations l'emportent sur les précédentes :

 
Sélectionnez
sub preparer_coupe_glace
{
    my %parametres =
    (
        parfum      => 'Vanille',
        garniture   => 'caramel',
        saupoudrage => 100,
        @_,
    );
    ...
}

IV-G-8. Verrouiller des tables de hachage

Comme les clés de hachage sont des barewords (des « mots nus »), elles offrent peu de protection typographique par rapport à la protection des noms de fonctions et de variables offerte par le pragma strict. Le module standard peu utilisé Hash::Util peut contribuer à sécuriser les tables de hachage.

Pour empêcher quelqu'un d'ajouter accidentellement une clé de hachage non voulue (que ce soit par une faute de frappe ou la saisie d'un utilisateur non digne de confiance), utilisez la fonction lock_keys() pour figer les clés du hachage à son ensemble de clés actuel. Toute tentative d'ajout d'une nouvelle clé déclenchera une exception. De même, vous pouvez verrouiller ou déverrouiller la valeur existante d'une clé donnée de la table de hachage (lock_value() et unlock_value()) et rendre la table entière en lecture seule avec lock_hash() ou disponible en écriture avec unlock_hash().

C'est une sécurité assez laxiste ; n'importe qui peut utiliser les fonctions de déverrouillage appropriées pour contourner le mécanisme. Pourtant, il protège contre les fautes de frappe et d'autres comportements accidentels.

IV-H. Coercition

Au long de sa durée de vie, une variable Perl peut contenir des valeurs de différents types — chaînes, entiers, nombres rationnels et plus encore. Plutôt que d'attacher les informations de type aux variables, Perl s'appuie sur le contexte fourni par les opérateurs (Contextes numériques, de chaîne et booléenContextes numériques, de chaîne et booléen) pour déterminer comment gérer les valeurs. Par conception, Perl tente de faire ce que vous voulez (DWIM, do what I mean), mais vous devez préciser vos intentions. Si vous traitez comme une chaîne une variable contentant un nombre, Perl fera de son mieux pour convertir ce nombre en une chaîne.

IV-H-1. Coercition booléenne

La coercition booléenne se produit lorsque vous testez si une valeur est vraie ou fausse, comme dans une condition if ou while. Le 0 numérique, undef, la chaîne vide et la chaîne '0' sont toutes évaluées comme étant des valeurs fausses. Toutes les autres valeurs, y compris les chaînes qui peuvent être numériquement égales à zéro (comme '0.0', '0e' et '0 but true'), sont vraies.

Quand un scalaire a les deux composantes, chaîne et numérique (DualvarsDualvars), Perl préfère évaluer la valeur booléenne de la composante chaîne. '0 but true' est évaluée à zéro en contexte numérique, mais n'étant pas une chaîne vide, elle est évaluée à la valeur vraie en contexte booléen.

IV-H-2. Coercition des chaînes de caractères

La coercition des chaînes de caractères se produit lorsque vous utilisez des opérateurs de chaîne comme les comparateurs (eq et cmp), la concaténation, split, substr, les expressions régulières et aussi lors de l'utilisation d'une valeur ou une expression comme clé de hachage. Les valeurs non initialisées sont évaluées comme chaînes vides, mais produisent un avertissement « use of unitialized value ... ». Les nombres sont convertis en chaînes contenant leur valeur, de sorte que la valeur 10 devient la chaîne 10. Vous pouvez même tronçonner un nombre en ses chiffres individuels avec split :

 
Sélectionnez
my @chiffres = split '', 1234567890;

IV-H-3. Coercition numérique

La coercition numérique se produit lorsque vous utilisez des opérateurs de comparaison numérique (tels que == et <=>), lors des opérations arithmétiques ou mathématiques et lors de l'utilisation d'une valeur ou une expression comme index de tableau ou de liste. Les valeurs non initialisées sont converties en zéro et produisent un avertissement « Utilisation de valeur non initialisée ». Les chaînes qui ne commencent pas par des séquences numériques sont également converties à zéro et produisent un avertissement « L'argument est non numérique ». Les chaînes qui commencent par des caractères autorisés dans les chaînes numériques sont converties en ces valeurs et ne produisent pas d'avertissement, de sorte que 10 leptons leaping devient 10 et 6.022e23 moles marauding devient 6.022e23.

Le module du noyau Scalar::Util contient une fonction looks_like_number() qui utilise les mêmes règles d'analyse que la grammaire de Perl pour extraire un nombre d'une chaîne.

Les mathématiciens se réjouissent

Les chaînes Inf et Infinity représentent la valeur infinie et se comportent comme des nombres. La chaîne NaN (not a number) représente le concept « pas un nombre ». Leur conversion en nombre ne produit pas l'avertissement « L'argument est non numérique ». Méfiez-vous, car les idées de Perl de l'infini et de « pas un nombre » peuvent ne pas correspondre aux idées de votre plate-forme ; ces notions ne sont pas toujours portables sur différents systèmes d'exploitation. Perl est cohérent, même si le reste de l'Univers ne l'est pas.

IV-H-4. Coercition des références

L'utilisation d'une opération de déréférencement sur une non-référence transforme cette valeur en référence. Ce processus d'autovivification (AutovivificationAutovivification) est utile lors de la manipulation des structures de données imbriquées (Structures de données imbriquéesStructures de données imbriquées) :

 
Sélectionnez
my %users;

$users{Brad}{id} = 228;
$users{Jack}{id} = 229;

Bien que la table de hachage ne contienne pas des valeurs pour Brad et Jack, Perl a utilement créé des références de hachage pour eux, puis a attribué à chacun une paire clé/valeur ayant comme clé l'id.

IV-H-5. Coercition de variables mises en cache

La représentation interne des valeurs Perl stocke à la fois des valeurs chaîne et numériques. La conversion en chaîne d'une valeur numérique ne remplace pas la valeur numérique. Au lieu de cela, elle ajoute une valeur chaîne de caractères à la représentation interne, qui contient alors les deux composantes. De même, la conversion d'une chaîne en nombre remplit la composante numérique, tout en laissant intacte la composante chaîne.

En Perl, certaines opérations préfèrent utiliser un composant d'une valeur plutôt que l'autre — par exemple, les vérifications booléennes préfèrent les chaînes. Si une valeur a en cache une représentation sous un format auquel vous ne vous attendez pas, compter sur une conversion implicite peut produire des résultats surprenants. Vous n'avez presque jamais besoin de dire explicitement ce que vous attendez. L'auteur peut se souvenir d'avoir dû le faire deux fois en quinze ans de programmation Perl, mais savoir que cette mise en cache se produit pourrait un jour vous aider à diagnostiquer une situation étrange.

IV-H-6. Dualvars

La nature multicomposante des valeurs Perl est disponible aux utilisateurs sous la forme de dualvars. Le module standard Scalar::Util fournit une fonction dualvar() qui permet de contourner la coercition de Perl et de manipuler séparément les composantes littérale et numérique d'une valeur :

 
Sélectionnez
use Scalar::Util 'dualvar';
my $faux_nom = dualvar 0, 'Sparkles & Blue';

say 'Boolean true!'  if        !! $faux_nom;
say 'Numeric false!' unless  0  + $faux_nom;
say 'String true!'   if     ''  . $faux_nom;

IV-I. Paquetages

Un espace de noms Perl associe et encapsule diverses entités nommées dans une catégorie nommée. C'est comme votre nom de famille ou un nom de marque. Contrairement à un nom dans le monde réel, un espace de noms n'implique aucune relation directe entre les entités. Ces relations peuvent exister, mais ce n'est pas obligatoire.

En Perl, un paquetage package ») est une collection de code dans un seul espace de noms. La distinction est subtile : le paquetage représente le code source et l'espace de noms représente l'entité créée lorsque Perl analyse ce code.

La fonction interne package déclare un paquetage et un espace de noms :

 
Sélectionnez
package MyCode;

our @boxes;

sub add_box { ... }

Toutes les variables globales et les fonctions déclarées ou référencées après la déclaration de paquetage désignent des symboles dans l'espace de noms MyCode. Vous pouvez référencer la variable @boxes à partir de l'espace de noms main seulement par son nom complet @MyCode::boxes. Un nom complet inclut un nom complet du paquetage, de sorte que vous pouvez appeler la fonction add_box() uniquement par MyCode::add_box().

La portée d'un paquetage se poursuit jusqu'à la prochaine déclaration package ou la fin du fichier, selon la première éventualité. Avec package, vous pouvez fournir un bloc qui délimite explicitement la portée de la déclaration :

 
Sélectionnez
package Flipper::Expert
{
    our $VERSION = 1969;
}

Le paquetage par défaut est le paquetage main. Sans une déclaration de paquetage, le paquetage courant est main. Cette règle s'applique aux scripts unilignes, aux programmes autonomes et même aux fichiers .pm.

En plus d'un nom, un paquetage dispose d'une version et trois méthodes implicites, import() (ImportationImportation), unimport() et VERSION(). VERSION() retourne le numéro de version du paquetage. Ce numéro est une série de chiffres contenus dans une variable globale du paquetage nommée $VERSION. Par convention, les versions sont une série de nombres entiers séparés par des points, comme 1.23 ou 1.1.10.

Perl inclut une syntaxe plus stricte pour les numéros de version, comme documenté dans perldoc version::Internals. Ces numéros de version doivent commencer par le caractère v et avoir au moins trois nombres entiers séparés par des points :

 
Sélectionnez
package MyCode v1.2.1;

Combiné avec la forme de bloc d'une déclaration package, vous pouvez écrire :

 
Sélectionnez
package Flipper::Expert v1969.3.7 { ... }

Cette syntaxe est encore rare, cependant. Vous êtes plus susceptible de voir la version pré-5.14 :

 
Sélectionnez
package MyCode;

our $VERSION = 1.21;

Chaque paquetage hérite d'une méthode VERSION() de la classe de base UNIVERSAL. Vous pouvez redéfinir VERSION(), mais il y a peu de raisons de le faire. Cette méthode renvoie la valeur de $VERSION :

 
Sélectionnez
my $version = Some::Plugin->VERSION;

Si vous fournissez un numéro de version comme argument, cette méthode lèvera une exception si la version du module n'est pas égale ou supérieure à l'argument :

 
Sélectionnez
# nécessite au moins 2.1
Some::Plugin->VERSION( 2.1 );

die "Votre plugin $version est trop ancien"
    unless $version > 2;

IV-I-1. Paquetages et espaces de noms

Chaque déclaration package crée un nouvel espace de noms, si nécessaire, et oblige l'analyseur à mettre tous les symboles globaux (variables globales et fonctions) ultérieurs du paquetage dans cet espace de noms.

Perl a des espaces de noms ouverts. Vous pouvez ajouter des fonctions ou variables dans un espace de noms à tout moment que ce soit avec une nouvelle déclaration de paquetage :

 
Sélectionnez
package Pack
{
    sub first_sub { ... }
}

Pack::first_sub();

package Pack
{
    sub second_sub { ... }
}

Pack::second_sub();

... ou en spécifiant les noms complets des fonctions au moment de la déclaration :

 
Sélectionnez
# implicite
package main;

sub Pack::third_sub { ... }

Vous pouvez enrichir un paquetage à tout moment lors de la compilation ou de l'exécution, quel que soit le fichier courant, même si la constitution d'un paquetage de plusieurs déclarations séparées (dans plusieurs fichiers !) peut rendre le code difficile à explorer.

Les espaces de noms peuvent avoir autant de niveaux que votre schéma d'organisation le requiert, bien que les espaces de noms ne soient pas hiérarchisés. La seule relation entre paquetages est sémantique et pas technique. Beaucoup de projets et d'entreprises créent leurs propres espaces de noms de haut niveau. Cela réduit la possibilité de conflits globaux et aide à organiser le code sur le disque. Par exemple :

  • StrangeMonkey est le nom du projet ;
  • StrangeMonkey::UI organise le code de l'interface utilisateur ;
  • StrangeMonkey::Persistence organise le code de la gestion des données ;
  • StrangeMonkey::Test organise le code de test pour le projet ;

... et ainsi de suite. Il s'agit d'une convention, mais elle est utile.

IV-J. Références

Perl fait habituellement ce que vous attendez, même si ce que vous attendez est subtil. Pensez à ce qui se passe quand vous passez des valeurs aux fonctions :

 
Sélectionnez
sub reverse_salutation
{
    my $nom = reverse shift;
    return "Bonjour, $nom !";
}

my $nom = 'Charles';
say reverse_salutation( $nom );
say $nom;

À l'extérieur de la fonction, $nom contient Charles, même si la valeur à l'intérieur de la fonction est inversée en selrahC. Vous vous y attendiez probablement. La valeur de $nom en dehors de la fonction est séparée de $nom à l'intérieur de la fonction. La modification de l'une est sans effet sur l'autre.

Pensez à l'autre possibilité. Si vous aviez à faire des copies de chaque valeur avant que quoi que ce soit puisse éventuellement les modifier, vous auriez à écrire beaucoup de code supplémentaire très défensif.

Il est parfois utile de modifier les valeurs en place. Si vous souhaitez passer une table de hachage pleine de données à une fonction pour la modifier, créer et retourner une nouvelle table pour chaque changement pourrait être fastidieux (sans parler d'inefficacité).

Perl fournit un mécanisme permettant de référencer une valeur sans faire une copie. Toute modification apportée à cette référence mettra à jour la valeur en place, de sorte que toutes les références vers cette valeur voient la valeur modifiée. Une référence est un type scalaire de données de première classe qui se réfère à un autre type de données de première classe.

IV-J-1. Références scalaires

L'opérateur de référencement est l'antislash (\). Dans un contexte scalaire, il crée une référence unique qui référence une autre valeur. Dans un contexte de liste, il crée une liste de références. Pour obtenir une référence à $nom :

 
Sélectionnez
my $nom     = 'Larry';
my $nom_ref = \$nom;

Vous devez déréférencer une référence pour évaluer la valeur à laquelle elle se réfère. Le déréférencement nécessite l'ajout d'un sigil supplémentaire pour chaque niveau de déréférencement :

 
Sélectionnez
sub reverse_in_place
{
    my $nom_ref = shift;
    $$nom_ref   = reverse $$nom_ref;
}

my $nom = 'Caroline';
reverse_in_place( \$nom );
say $nom;     # imprime "eniloraC"

Le sigil scalaire double ($$) déréférence une référence scalaire.

Tandis que dans @_ les paramètres se comportent comme des alias des variables de l'appelante, rappelez-vous que les boucles for produisent un comportement similaire à l'aliasing (Itération et aliasingItération et aliasing), de sorte que vous pouvez les modifier en place :

 
Sélectionnez
sub reverse_valeur_in_place
{
    $_[0] = reverse $_[0];
}

my $nom = 'allizocohC';
reverse_valeur_in_place( $nom );
say $nom;

Habituellement, vous ne voulez pas modifier les valeurs de cette façon — les fonctions appelantes s'y attendent rarement, par exemple. L'assignation de paramètres aux variables lexicales dans vos fonctions supprime ce comportement d'aliasing.

Économiser la mémoire avec des références

La modification d'une valeur en place, ou le retour d'une référence à un scalaire peut économiser de la mémoire. Comme Perl copie les valeurs à l'initialisation, vous pourriez vous retrouver avec plusieurs copies d'une chaîne longue. Le passage par référence signifie que Perl va uniquement copier les références — une opération beaucoup moins coûteuse. Avant de modifier votre code pour n'utiliser que des références, cependant, mesurez les différences pour voir si cela changera vraiment les choses.

Les références complexes peuvent nécessiter un bloc entouré par des accolades pour éviter l'ambiguïté des certaines parties de l'expression. Vous pouvez utiliser toujours cette syntaxe, bien que parfois elle clarifie et d'autres fois elle obscurcit :

 
Sélectionnez
sub reverse_in_place
{
    my $nom_ref   = shift;
    ${ $nom_ref } = reverse ${ $nom_ref };
}

Si vous oubliez de déréférencer une référence scalaire, Perl va probablement convertir la référence en une valeur de chaîne de la forme SCALAR(0x93339e8) ou en une valeur numérique comme 0x93339e8. Cette valeur indique le type de référence (en l'occurrence, scalaire) et l'emplacement en mémoire de la référence... pas que ce soit utile pour quelque chose au-delà de la distinction entre références.

Les références ne sont pas des pointeurs

Perl n'offre pas un accès natif aux emplacements en mémoire. L'adresse de la référence est une valeur utilisée comme identifiant. Contrairement aux pointeurs dans un langage comme le C, vous ne pouvez pas modifier l'adresse d'une référence ou la traiter comme une adresse en mémoire. Ces adresses ne sont uniques que la plupart du temps, parce que Perl peut réutiliser les emplacements de stockage quand il récupère de la mémoire inutilisée.

IV-J-2. Références de tableaux

Les références de tableaux sont utiles dans plusieurs circonstances :

  • pour passer à des fonctions et retourner des tableaux de fonctions sans aplatissement en liste unique ;
  • pour créer des structures de données multidimensionnelles ;
  • pour éviter les copies de tableau inutiles ;
  • pour maintenir des structures de données anonymes.

Utilisez l'opérateur de référence pour créer une référence à un tableau déclaré :

 
Sélectionnez
my @cartes     = qw( R D V 10 9 8 7 6 5 4 3 2 A );
my $cartes_ref = \@cartes;

Toute modification apportée par le biais de $cartes_ref va modifier @cartes et vice versa. Vous pouvez accéder au tableau dans son ensemble avec le sigil @, pour l'aplatir dans une liste (contexte de liste) ou compter ses éléments (contexte scalaire) :

 
Sélectionnez
my $compter_carte = @$cartes_ref;
my @copie_carte   = @$cartes_ref;

Accédez aux éléments individuels à l'aide de la flèche de déréférencement (->) :

 
Sélectionnez
my $premiere_carte = $cartes_ref->[0];
my $derniere_carte = $cartes_ref->[-1];

La flèche est nécessaire pour distinguer entre un scalaire nommé $cartes_ref et un tableau nommé @cartes_ref. Notez l'utilisation du sigil scalaire (Sigils de variablesSigils de variables) pour accéder à un seul élément.

Doubler les sigils

Une syntaxe alternative ajoute un autre sigil scalaire à la référence du tableau. Il est plus court, mais moins joli d'écrire my $premiere_carte = $$cartes_ref [0];.

Utilisez la syntaxe de déréférencement avec accolades pour obtenir une tranche (Tranches de tableauxTranches de tableaux) d'une référence de tableau :

 
Sélectionnez
my @hautes_cartes = @{ $cartes_ref }[0 .. 2, -1];

Vous pouvez omettre les accolades, mais souvent elles améliorent la lisibilité.

Pour créer un tableau anonyme — sans utiliser un tableau déclaré, entourez par des crochets une liste de valeurs ou une expression générant une liste :

 
Sélectionnez
my $deguisements_ref = [qw( Singes Robots Dinosaures Fromage )];

Cette référence de tableau se comporte comme les références de tableaux nommés, sauf que les crochets du tableau anonyme créent toujours une nouvelle référence. Une référence de tableau nommé créée à l'intérieur de la portée de celui-ci se réfère toujours au même tableau (ce qui peut conduire à des bogues délicats à élucider si l'on n'y prend garde). Par exemple :

 
Sélectionnez
my @repas        = qw( soupe sandwiches pizza );
my $dimanche_ref = \@repas;
my $lundi_ref    = \@repas;

push @repas, 'crème glacée';

... tant $dimanche_ref que $lundi_ref contiennent maintenant un dessert, alors que :

 
Sélectionnez
my @repas        = qw( soupe sandwiches pizza );
my $dimanche_ref = [ @repas ];
my $lundi_ref    = [ @repas ];

push @repas, 'tarte aux baies rouges';

... ni $dimanche_ref ni $lundi_ref ne contiennent un dessert. Entre les crochets utilisés pour créer le tableau anonyme, un contexte de liste aplatit le tableau @repas dans une nouvelle liste sans rapport avec @repas.

IV-J-3. Références de hachages

Utilisez l'opérateur de référence sur une table de hachage nommée pour créer une référence de hachage :

 
Sélectionnez
my %couleurs = (
    'bleu'     => 'azul',
    'or'       => 'dorado',
    'rouge'    => 'rojo',
    'jaune'    => 'amarillo',
    'pourpre'  => 'morado',
);

my $couleurs_ref = \%couleurs;

Accédez aux clés ou aux valeurs d'une table de hachage en faisant précéder la référence par le sigil de table de hachage % :

 
Sélectionnez
my @francais_couleurs = keys   %$couleurs_ref;
my @espagnol_couleurs = values %$couleurs_ref;

Accédez à des valeurs individuelles de la table de hachage (pour stocker, supprimer, vérifier l'existence ou récupérer) en utilisant la flèche de déréférencement ou le double sigil :

 
Sélectionnez
sub traduire_en_espagnol
{
    my $couleur = shift;
    return $couleurs_ref->{$couleur};
    # ou return $$couleurs_ref{$couleur};
}

Utilisez le sigil de tableau (@) et des accolades (afin d'éliminer l'ambiguïté) pour « trancher » une référence de hachage :

 
Sélectionnez
my @couleurs = qw( rouge bleu jaune );
my @colores  = @{ $couleurs_ref }{@couleurs};

Créez des tables de hachage anonymes en place avec des accolades :

 
Sélectionnez
my $nourriture_ref = {
    'gâteau d\'anniversaire' => 'la torta de cumpleaños',
    bonbons                  => 'dulces',
    cupcake                  => 'bizcochito',
    'crème glacée'           => 'helado',
};

Comme pour les tableaux anonymes, les tables de hachage anonymes créent une nouvelle table de hachage anonyme à chaque exécution.

Gare aux accolades !

L'erreur fréquente des débutants d'assigner une table de hachage anonyme à une table de hachage standard génère un avertissement sur un nombre impair d'éléments dans la table de hachage. Utilisez des parenthèses pour une table de hachage nommée et des accolades pour une table de hachage anonyme.

IV-J-4. Références de fonctions

Perl propose des fonctions de première classe, en ce sens qu'une fonction est un type de données, tout comme un tableau ou une table de hachage. En d'autres termes, Perl prend en charge les références de fonctions. Cela permet de nombreuses fonctionnalités avancées (Fonctions de rappel, Fermetures FermeturesFermetures). Créez une référence de fonction en utilisant l'opérateur de référencement et le sigil de fonction (&) devant le nom d'une fonction :

 
Sélectionnez
sub cuire_gateau { say 'Cuire un merveilleux gâteau !' };

my $gateau_ref = \&cuire_gateau;

Sans le sigil de fonction (&), vous obtiendriez une référence à la valeur ou aux valeurs de retour de la fonction.

Créez des fonctions anonymes avec le mot clé sub :

 
Sélectionnez
my $tarte_ref = sub { say 'Faire une tarte délicieuse !' };

L'utilisation de l'instruction interne sub sans nom de fonction compile la fonction, mais ne l'installe pas dans l'espace de noms courant. La seule façon d'accéder à cette fonction est par l'intermédiaire de la référence retournée par sub. Invoquez la référence de la fonction par la flèche de déréférencement :

 
Sélectionnez
$gateau_ref->();
$tarte_ref->();

Appels de fonctions en Perl 4

Une syntaxe alternative d'invocation des références de fonction utilise le sigil de fonction (&) au lieu de la flèche de déréférencement. Évitez-la : elle a des implications subtiles pour l'analyse et le passage des arguments.

Considérez les parenthèses vides comme à une invocation de l'opération de déréférencement, de la même manière que les crochets indiquent une recherche par indice (dans un tableau) et les accolades indiquent une recherche par clé (dans une table de hachage). Passez des arguments à la fonction entre parenthèses :

 
Sélectionnez
$cuire_quelque_chose_ref->( 'cupcakes' );

Vous pouvez également utiliser des références de fonction en tant que méthodes avec des objets (MooseMoose). Cela est utile lorsque vous avez déjà recherché la méthode dans l'arbre d'héritage (RéflexionRéflexion) :

 
Sélectionnez
my $propre = $robot_menage->can( 'nettoyer' );
$robot_menage->$propre( $cuisine );

IV-J-5. Références de descripteur de fichier

Lorsque vous utilisez la forme lexicale de descripteur de fichier d'open (et d'opendir), vous utilisez des références de descripteurs de fichiers. En interne, ces descripteurs de fichiers sont des objets de la classe IO::File. Vous pouvez appeler des méthodes directement sur eux :

 
Sélectionnez
use autodie 'open';

open my $out_fh, '>', 'output_file.txt';
$out_fh->say( 'Prenez du texte !' );

Le code ancien peut contenir use IO::Handle;. Le code encore plus ancien pourrait obtenir des références vers des typeglobs :

 
Sélectionnez
local *FH;
open FH, "> $file" or die "Écriture impossible dans '$file': $ !";
my $fh = \*FH;

Cet idiome est antérieur aux descripteurs de fichiers lexicaux introduits avec Perl 5.6.0 en mars 2000, alors ce code est coincé dans le millénaire précédent. Vous pouvez toujours utiliser l'opérateur de référence sur typeglobs pour obtenir des références aux descripteurs de fichiers des paquetages globaux tels que STDIN, STDOUT, STDERR ou DATA, mais de toute façon ce sont tous les noms globaux.

Préférez les descripteurs de fichiers lexicaux lorsque c'est possible. Avec l'avantage de la portée explicite, les descripteurs de fichiers lexicaux vous permettent de gérer la durée de vie des descripteurs de fichiers comme une fonctionnalité de gestion de la mémoire de Perl.

IV-J-6. Comptage des références

Perl utilise une technique de gestion de la mémoire connue sous le nom de comptage des références. Chaque valeur Perl possède un compteur attaché. Perl incrémente ce compteur chaque fois que quelque chose prend une référence vers la valeur, implicitement ou explicitement. Perl décrémente ce compteur chaque fois qu'une référence disparaît. Lorsque le compteur atteint zéro, Perl sait qu'il peut recycler cette valeur en toute sécurité. Considérons le descripteur de fichier ouvert dans cette portée intérieure :

 
Sélectionnez
say 'fichier pas ouvert';

{
    open my $fh, '>', 'portée_intérieure.txt';
    $fh->say( 'fichier ouvert ici' );
}

say 'fichier fermé ici';

Dans le bloc interne dans l'exemple, il y a une seule variable $fh. (Elle est mentionnée sur plusieurs lignes dans le code source, mais c'est une seule variable, nommée $fh.) $fh existe seulement dans la portée du bloc. Sa valeur ne quitte jamais le bloc. Lorsque l'exécution atteint la fin du bloc, Perl recycle la variable $fh et diminue le nombre de références du descripteur de fichier renvoyé par $fh. Le compteur de références du descripteur de fichier atteint zéro, alors Perl le recycle pour récupérer la mémoire et appelle close() implicitement.

Vous n'avez pas besoin de comprendre les détails de la façon dont tout cela fonctionne. Vous devez seulement comprendre que vos actions de créer des références et de les transmettre à gauche et à droite affectent la façon dont Perl gère la mémoire (voir Références circulairesRéférences circulaires).

IV-J-7. Références et fonctions

Lorsque vous utilisez des références comme arguments des fonctions, documentez soigneusement votre intention. La modification des valeurs de référence dans une fonction peut surprendre le code appelant, qui ne s'attendait jamais à ce qu'autre chose modifie ses données. Pour modifier le contenu d'une référence sans affecter la référence elle-même, copiez ses valeurs dans une nouvelle variable :

 
Sélectionnez
my @nouveau_tableau         = @{ $tableau_ref };
my %nouvelle_table_hachage  = %{ $table_hachage_ref  };

Ceci est nécessaire uniquement dans quelques cas, mais le clonage explicite permet d'éviter de mauvaises surprises pour le code appelant. Si vous utilisez des structures de données imbriquées ou d'autres références complexes, envisagez l'utilisation du module standard Storable et sa fonction dclone (clonage profond).

IV-K. Structures de données imbriquées

Les types de données agrégés de Perl — tableaux et tables de hachage — vous permettent de stocker des scalaires indexés par des entiers ou clés littérales. Notez bien le mot scalaire. Si vous tentez de stocker un tableau dans un tableau, l'aplatissement automatique en liste de Perl transformera tout en un seul tableau :

 
Sélectionnez
my @comptine    = qw( am   stram gram   );
my @canards     = qw( riri fifi  loulou );
my @personnages = qw( tintin milou haddock  );

my @fameux_triplets = (
    @comptine, @canards, @personnages
); # @fameux_triplets est maintenant un seul tableau de 9 éléments

Perl résout ce problème à l'aide des références (RéférencesRéférences), des scalaires spéciaux qui peuvent référencer d'autres variables (scalaires, tableaux et tables de hachage). En Perl, les structures de données imbriquées, comme un tableau de tableaux ou un hachage de hachages, sont rendues possibles par l'utilisation des références. Celles-ci sont utiles et vous devez les comprendre, mais rien ne vous oblige à aimer leur syntaxe, qui est l'une des caractéristiques les plus laides de Perl.

Utilisez l'opérateur de référence, \, pour créer une référence vers une variable nommée :

 
Sélectionnez
my @fameux_triplets = (
    \@comptine, \@canards, \@personnages
);

... ou la syntaxe de déclaration d'une référence anonyme afin d'éviter l'utilisation des variables nommées :

 
Sélectionnez
my @fameux_triplets = (
    [qw( am   stram gram    )],
    [qw( riri fifi  loulou  )],
    [qw( tintin milou haddock )],
);

my %repas = (
    petit_dejeuner => { entree  => 'oeufs',
                        plat    => 'pommes de terre'},
    dejeuner       => { entree  => 'panini',
                        plat    => 'pomme'          },
    diner          => { entree  => 'steak',
                        plat    => 'salade'         },
);

Les virgules sont libres et gratuites

Perl autorise une virgule optionnelle après le dernier élément d'une liste. Cela rend plus facile l'ajout d'autres éléments à l'avenir.

Utilisez la syntaxe de référence de Perl pour accéder aux éléments des structures de données imbriquées. Le sigil désigne la quantité de données à récupérer. La flèche de déréférencement indique que la valeur d'une partie de la structure de données est une référence :

 
Sélectionnez
my $dernier_neveu = $fameux_triplets[1]->[2];
my $repas_plat    = $repas{petit_dejeuner}->{plat};

La seule façon d'imbriquer une structure de données à plusieurs niveaux est par le biais des références, donc la flèche dans les exemples précédents est superflue. Vous pouvez l'omettre pour plus de clarté, sauf pour invoquer des références de fonction :

 
Sélectionnez
my $neveu = $fameux_triplets[1][2];
my $repas = $repas{petit_dejeuner}{plat};

$actions{genereux}{achat_nourriture}->( $neveu, $repas );

Utilisez des blocs pour éviter l'ambiguïté lors de l'accès aux composants de structures de données imbriquées comme si elles étaient des tableaux ou hachages de première classe :

 
Sélectionnez
my $neveu_compter = @{ $fameux_triplets[1] };
my $diner_courses = keys %{ $repas{diner} };

... ou pour créer une tranche de structure de données imbriquées :

 
Sélectionnez
my ($entree, $plat) = 
 @{ $repas{petit_dejeuner} }{ qw( entree plat ) };

L'ajout d'espaces facilite la lecture, mais il n'élimine pas complètement le bruit de cette construction. Parfois, une variable temporaire apporte plus de clarté :

 
Sélectionnez
my $repas_ref       = $repas{petit_dejeuner};
my ($entree, $plat) = @$repas_ref{qw( entree plat )};

... ou utilisez l'aliasing implicite de la boucle for pour éviter l'utilisation d'une référence intermédiaire (remarquez l'absence de my) :

 
Sélectionnez
($entree, $plat) = @{ $_ }{qw( entree plat )}
                   for $repas{petit_dejeuner};

perldoc perldsc, le livre de recettes des structures de données, donne de nombreux exemples d'utilisation des différentes structures de données de Perl.

IV-K-1. Autovivification

Lorsque vous essayez d'écrire dans un composant d'une structure de données imbriquées, Perl créera au besoin le chemin à travers la structure de données vers la destination :

 
Sélectionnez
my @aoaoaoa;
$aoaoaoa[0][0][0][0] = 'profondément imbriqué';

Après la deuxième ligne de code, ce tableau de tableaux de tableaux de tableaux contient une référence de tableau dans une référence de tableau dans une référence de tableau dans une référence de tableau. Chaque référence de tableau contient un élément.

De même, lorsque vous demandez à Perl de traiter une valeur non initialisée comme s'il s'agissait d'une référence de table de hachage, Perl transformera cette valeur non définie dans une référence de table de hachage :

 
Sélectionnez
my %hohoh;
$hohoh{Robot}{Santa} = 'surtout nuisible';

Ce comportement s'appelle l'autovivification. Il présente l'avantage de réduire le code d'initialisation des structures de données imbriquées, mais il ne peut pas faire la distinction entre l'intention honnête de créer des éléments manquants dans les structures de données imbriquées et une faute de frappe accidentelle.

Vous pouvez être étonnés par la contradiction entre le bénéfice de l'autovivification et l'activation du pragma strict. C'est une question d'équilibre. Est-il plus pratique d'intercepter les erreurs qui changent le comportement de votre programme au détriment de la désactivation des contrôles d'erreur pour quelques références symboliques bien encapsulées ? Est-il plus commode de permettre aux structures de données de croître ou plus sûr d'exiger une taille fixe et un ensemble permis de clés ?

Contrôler l'autovivification

Le pragma autovivification (PragmasPragmas) du CPAN vous permet de désactiver l'autovivification à l'intérieur d'une portée lexicale pour des types spécifiques d'opérations.

Les réponses dépendent de votre projet. Au début du développement, laissez-vous la liberté d'expérimenter. Lors des tests et du déploiement, envisagez une augmentation de rigueur pour éviter des effets secondaires indésirables. Grâce à la portée lexicale des pragma strict et autovivification, vous pouvez activer ces comportements où — et si — nécessaire.

Vous pouvez vérifier vos attentes avant de déréférencer chaque niveau d'une structure de données complexe, mais le code qui en résulte est souvent long et fastidieux. Il est préférable d'éviter les structures de données fortement imbriquées en révisant votre modèle de données pour fournir une meilleure encapsulation.

IV-K-2. Débogage des structures de données imbriquées

La complexité de la syntaxe de déréférencement de Perl combinée avec la possibilité de confusion avec plusieurs niveaux de références peut rendre difficile le débogage des structures de données imbriquées. Il existe deux bons outils de visualisation.

Le module standard Data::Dumper convertit des valeurs de complexité arbitraire en chaînes de code Perl :

 
Sélectionnez
use Data::Dumper;

print Dumper( $my_complex_structure );

Utilisez cette option lorsque vous avez besoin de comprendre ce qu'une structure de données contient, à quoi vous devez accéder et à quoi vous avez accédé à la place. Data::Dumper peut afficher à l'écran des objets ainsi que des références de fonctions (si vous donnez une valeur vraie à $Data::Dumper::Deparse).

Data::Dumper présente l'avantage d'être un module du noyau et affiche du code Perl, mais sa sortie est verbeuse. Certains développeurs préfèrent l'utilisation des modules YAML::XS ou JSON pour débogage. Ils ne produisent pas de code Perl, mais leurs sorties peuvent être beaucoup plus claires à lire et à comprendre.

IV-K-3. Références circulaires

Le système de comptage de références de la gestion de mémoire Perl (Comptage des référencesComptage des références) présente un inconvénient. Deux références qui pointent l'une vers l'autre (directement ou indirectement) forment une référence circulaire que Perl ne peut pas détruire tout seul. Considérons un modèle genéalogique, où chaque entité a deux parents et zéro, un ou plusieurs enfants :

 
Sélectionnez
my $alice  = { mere => '',     pere => ''     };
my $robin  = { mere => '',     pere => ''     };
my $cianne = { mere => $alice, pere => $robin };

push @{ $alice->{enfants} }, $cianne;
push @{ $robin->{enfants} }, $cianne;

Tant $alice que $robin contiennent une référence à un tableau qui contient $cianne. Comme $cianne est une référence de table de hachage qui contient $alice et $robin, Perl ne décrémentera jamais à zéro le nombre de références à l'une de ces trois personnes. Il ne reconnaît pas que ces références circulaires existent et il ne peut pas gérer la durée de vie de ces entités.

Vous pouvez soit interrompre vous-même manuellement le comptage de références (en effaçant les enfants de $alice et $robin ou les parents de $cianne), soit utiliser des références faibles. Une référence faible est une référence qui n'incrémente pas le compteur de références de la variable référencée. Utilisez la fonction weaken() du module standard Scalar::Util pour affaiblir une référence :

 
Sélectionnez
use Scalar::Util 'weaken';

my $alice  = { mere => '',     pere => ''     };
my $robin  = { mere => '',     pere => ''     };
my $cianne = { mere => $alice, pere => $robin };

push @{ $alice->{enfants} }, $cianne;
push @{ $robin->{enfants} }, $cianne;

weaken( $cianne->{mere} );
weaken( $cianne->{pere} );

$cianne gardera des références utilisables vers $alice et $robin, mais ces références faibles ne comptent pas dans le nombre des références restantes vers les parents. Si le compteur de références de $alice arrive à zéro, le ramasse-miettes de Perl récupérera la mémoire correspondant à son enregistrement, même si $cianne a une référence faible vers $alice. Soyez conscient que, lorsque $alice sera récupérée, la référence de $cianne vers $alice aura la valeur undef.

La plupart des structures de données n'ont pas besoin de références faibles, mais quand elles sont nécessaires, elles sont inestimables.

IV-K-4. Alternatives aux structures de données imbriquées

Tandis que Perl traitera sans rechigner des structures de données imbriquées aussi profondément que vous pouvez l'imaginer, le coût humain de la compréhension de ces structures de données et de leurs relations — sans parler de la syntaxe complexe — est élevé. Au-delà de deux ou trois niveaux d'imbrication, demandez-vous si une modélisation des divers composants de votre système en classes et objets (MooseMoose) rendra votre code plus clair.


précédentsommairesuivant
L'UTF-8 est un « format de transformation » (ou norme de codage) permettant de représenter l'intégralité des points de code (en simplifiant, l'intégralité des caractères) de l'Unicode. Il permet d'écrire la quasi-totalité des langues écrites dans le monde, y compris les signes de ponctuation et toutes sortes de caractères spéciaux comme @ ou .
Larry Wall, le créateur de Perl, considère la paresse comme l'une des trois vertus cardinales du développeur. Sans entrer dans les détails, le mot « paresse » peut ici être compris une recherche de l'efficacité maximale (efficacité de codage, efficacité d'exécution, etc.), ce qui peut parfois demander pas mal d'effort et de travail… Rien à voir, donc, avec la paresse que l'on peut parfois reprocher à un enfant qui ne fiche rien à l'école ou un salarié qui se décharge de son travail sur ses collègues. (NdT)

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Licence Creative Commons
Le contenu de cet article est rédigé par chromatic et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2015 Developpez.com.