Perl moderne : 2014

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

chromatic

Traducteurs

| Mishulyna | Laurent Rosenfeld |

Les programmeurs Perl résolvent des problèmes et trouvent des solutions. Perl est l'arme secrète qu'ils utilisent quand ils ont à résoudre un problème tout de suite.
Le Perl moderne élève cette productivité à un niveau supérieur. Apprenez la philosophie Perl pour libérer toute la puissance du langage. Découvrez le meilleur de ce que le CPAN a à offrir. Examinez comment les nouvelles fonctionnalités de Perl 5.16 et 5.18 permettront d'améliorer votre code et vous aideront à achever votre travail plus vite — et aussi à vous amuser.
Perfectionnez votre connaissance des outils et techniques. Perfectionnez vos compétences. Maîtrisez tous les avantages que Perl vous offre.
Quand vous avez à résoudre un problème tout de suite, tournez-vous vers le Perl moderne.

Édition 2014 - Mis à jour pour Perl 5.18 - chromatic.

Commentez

- Titre : Perl moderne : 2014

- Auteur : chromatic

- Traducteurs : Mishulyna, Laurent Rosenfeld

- Parution : 1 février 2015

- Public visé :

- Licence : 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 ® 2013 Developpez.com.

Sommaire

- I. Préface

- I-A. Exécuter du Perl moderne

- I-B. Remerciements

- II. La philosophie Perl

- II-A. Perldoc

- II-B. Expressivité

- II-C. Contexte

- II-C-1. Contexte vide, scalaire et de liste

- II-C-2. Contextes numériques, de chaîne et booléen

- II-D. Idées implicites

- II-D-1. La variable scalaire par défaut

- II-D-2. Les variables tableau par défaut

- III. Perl et sa communauté

- III-A. Le CPAN

- III-A-1. Outils de gestion CPAN

- III-B. Sites de la communauté

- III-C. Sites de développement

- III-D. Événements

- III-E. IRC

- IV. Le langage Perl

- IV-A. Noms

- IV-A-1. Noms de variables et sigils

- IV-A-2. Espaces de noms

- IV-B. Variables

- IV-B-1. Portée des variables

- IV-B-2. Sigils de variables

- IV-B-3. Variables anonymes

- IV-B-4. Variables, types et coercition

- IV-C. Valeurs

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

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

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

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

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

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

- IV-C-2-e. Conversion implicite

- IV-C-3. Nombres

- IV-C-4. Undef

- IV-C-5. La liste vide

- IV-C-6. Listes

- IV-D. Flot d'exécution

- IV-D-1. Les instructions conditionnelles

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

- IV-D-2-a. Court-circuit

- IV-D-3. Contexte des instructions conditionnelles

- IV-D-4. Boucles

- IV-D-5. Itération et aliasing

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

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

- IV-D-8. While et until

- IV-D-9. Boucles imbriquées

- IV-D-10. Contrôle de boucle

- IV-D-11. Continue

- IV-D-12. Instructions switch

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

- IV-E. Scalaires

- IV-E-1. Scalaires et types

- IV-F. Tableaux

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

- IV-F-2. Initialisation des tableaux

- IV-F-3. Opérations sur tableaux

- IV-F-4. Tranches de tableaux

- IV-F-5. Tableaux et contexte

- IV-F-6. Interpolation de tableau

- IV-G. Tables de hachage

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

- IV-G-2. Indexation des tables de hachage

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

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

- IV-G-5. Tranches de hachage

- IV-G-6. La table de hachage vide

- IV-G-7. Idiomes de tables de hachage

- IV-G-8. Verrouiller des tables de hachage

- IV-H. Coercition

- IV-H-1. Coercition booléenne

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

- IV-H-3. Coercition numérique

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

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

- IV-H-6. Dualvars

- IV-I. Paquetages

- IV-I-1. Paquetages et espaces de noms

- IV-J. Références

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

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

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

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

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

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

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

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

- IV-K-1. Autovivification

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

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

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

- V. Opérateurs

- V-A. Caractéristiques des opérateurs

- V-A-1. Ordre des opérations (priorité)

- V-A-2. Associativité

- V-A-3. Arité

- V-A-4. Fixité

- V-B. Types d'opérateurs

- V-B-1. Opérateurs numériques

- V-B-2. Opérateurs de chaîne de caractères

- V-B-3. Opérateurs logiques

- V-B-4. Opérateurs de bit

- V-B-5. Opérateurs spéciaux

- VI. Fonctions

- VI-A. Déclarer des fonctions

- VI-B. Appeler des fonctions

- VI-C. Paramètres de fonction

- VI-C-1. Aplatissement

- VI-C-2. Gourmandise

- VI-C-3. Gestion des alias

- VI-D. Fonctions et espaces de noms

- VI-D-1. Importation

- VI-E. Rapports d'erreurs

- VI-E-1. Valider les arguments

- VI-F. Fonctions avancées

- VI-F-1. Sensibilisation au contexte

- VI-F-2. Récursivité

- VI-F-3. Variables lexicales

- VI-F-4. Appels de fonctions et récursion terminale

- VI-G. Pièges et limitations

- VI-H. Portée

- VI-H-1. Portée lexicale

- VI-H-2. La portée our

- VI-H-3. Portée dynamique

- VI-H-4. Portée statique

- VI-I. Fonctions anonymes

- VI-I-1. Déclarer des fonctions anonymes

- VI-I-2. Noms de fonctions anonymes

- VI-I-3. Fonctions anonymes implicites

- VI-J. Fermetures

- VI-J-1. Création de fermetures

- VI-J-2. Utilisations des fermetures

- VI-J-3. Fermetures et application partielle

- VI-K. Variables statiques ou fermetures

- VI-L. Statique ou pseudostatique

- VI-M. Attributs

- VI-M-1. Inconvénients des attributs

- VI-N. AUTOLOAD

- VI-N-1. Renvoyer des méthodes dans AUTOLOAD()

- VI-N-2. Générer du code dans AUTOLOAD()

- VI-N-3. Inconvénients de AUTOLOAD

- VII. Expressions régulières et correspondance

- VII-A. Variables littérales

- VII-B. L'opérateur qr// et les combinaisons regex

- VII-C. Quantificateurs

- VII-D. Gourmandise

- VII-E. Ancres de regex

- VII-F. Métacaractères

- VII-G. Classes de caractères

- VII-H. Capturer

- VII-H-1. Captures nommées

- VII-H-2. Captures numérotées

- VII-I. Regroupement et alternatives

- VII-J. Autres séquences d'échappement

- VII-K. Assertions

- VII-L. Modificateurs de regex

- VII-M. Reconnaissances intelligentes

- VIII. Objets

- VIII-A. Moose

- VIII-A-1. Classes

- VIII-A-2. Méthodes

- VIII-A-3. Attributs

- VIII-A-4. Encapsulation

- VIII-A-4-a. Polymorphisme

- VIII-A-5. Rôles

- VIII-A-5-a. Rôles et DOES()

- VIII-A-6. Héritage

- VIII-A-6-a. Héritage et attributs

- VIII-A-6-b. Ordre de recherche des méthodes

- VIII-A-6-c. Héritage et méthodes

- VIII-A-6-d. Héritage et isa()

- VIII-A-7. Moose et Perl OO

- VIII-B. Références liées

- VIII-B-1. Recherche de méthode et héritage

- VIII-B-2. AUTOLOAD

- VIII-B-3. Surcharge de méthode et SUPER

- VIII-B-4. Stratégies pour faire face aux références liées

- VIII-C. Réflexion

- VIII-C-1. Vérifier si un module est chargé

- VIII-C-2. Vérifier si un paquetage existe

- VIII-C-3. Vérifier si une classe existe

- VIII-C-4. Vérifier le numéro de version d'un module

- VIII-C-5. Vérifier si une fonction existe

- VIII-C-6. Vérifier si une méthode existe

- VIII-C-7. Rechercher dans les tables de symboles

- VIII-D. Perl OO avancé

- VIII-D-1. Préférer la composition à l'héritage

- VIII-D-2. Principe de responsabilité unique

- VIII-D-3. Ne vous répétez pas

- VIII-D-4. Principe de substitution de Liskov

- VIII-D-5. Sous-types et coercitions

- VIII-D-6. Immutabilité

- IX. Style et efficacité

- IX-A. Écrire du Perl maintenable

- IX-B. Écrire du Perl idiomatique

- IX-C. Écrire du Perl efficace

- IX-D. Exceptions

- IX-D-1. Signalez les exceptions

- IX-D-2. Intercepter les exceptions

- IX-D-3. Dangers des exceptions

- IX-D-4. Exceptions internes

- IX-E. Pragmas

- IX-E-1. Pragmas et portée

- IX-E-2. Utiliser des pragmas

- IX-E-3. Pragmas utiles

- X. Gestion des programmes réels

- X-A. Tests

- X-A-1. Test::More

- X-A-2. Exécuter des tests

- X-A-3. Meilleures comparaisons

- X-A-4. Organiser des tests

- X-A-5. Autres modules de test

- X-B. Gérer les avertissements

- X-B-1. Produire des avertissements

- X-B-2. Activer et désactiver des avertissements

- X-B-3. Désactiver des catégories d'avertissements

- X-B-4. Rendre les avertissements fatals

- X-B-5. Intercepter des avertissements

- X-B-6. Enregistrer vos propres avertissements

- X-C. Fichiers

- X-C-1. Entrée et sortie

- X-C-1-a. Unicode, couches E/S et modes de fichier

- X-C-1-b. Fonction open à deux arguments

- X-C-1-c. Lire des fichiers

- X-C-1-d. Écrire dans des fichiers

- X-C-1-e. Fermer des fichiers

- X-C-1-f. Variables spéciales de gestion des fichiers

- X-C-2. Répertoires et chemins

- X-C-2-a. Manipuler des chemins de fichier

- X-C-3. Manipuler des fichiers

- X-D. Modules

- X-D-1. Utiliser et importer

- X-D-2. Exporter

- X-D-3. Organiser le code en modules

- X-E. Distributions

- X-E-1. Attributs d'une distribution

- X-E-2. Outils CPAN pour gérer les distributions

- X-E-3. Conception des distributions

- X-F. Le paquetage UNIVERSAL

- X-F-1. La Méthode VERSION()

- X-F-2. La méthode DOES()

- X-F-3. La méthode can()

- X-F-4. La méthode isa()

- X-F-5. Étendre UNIVERSAL

- X-G. Génération de code

- X-G-1. eval

- X-G-2. Fermetures paramétriques

- X-G-3. Manipulation lors de la compilation

- X-G-4. La classe Class::MOP

- X-H. Surcharge

- X-H-1. Surcharge des opérations courantes

- X-H-2. Surcharge et héritage

- X-H-3. Utilisations de surcharge

- X-I. Données d'origine non fiable et le mode taint

- X-I-1. Utilisation du mode taint

- X-I-2. Sources de contamination

- X-I-3. Décontamination de données

- X-I-4. Décontamination de l'environnement

- X-I-5. Les pièges et limites du mode taint

- XI. Perl au-delà de syntaxe

- XI-A. Expressions idiomatiques

- XI-A-1. L'objet comme $self

- XI-A-2. Paramètres nommés

- XI-A-3. La transformation de Schwartz

- XI-A-4. Lire un fichier d'un seul coup

- XI-A-5. Gérer Main

- XI-A-6. Exécution contrôlée

- XI-A-7. Validation postfixée des paramètres

- XI-A-8. Regex en passant

- XI-A-9. Coercitions unaires

- XI-B. Variables globales

- XI-B-1. Gérer les variables super globales

- XI-B-2. Noms anglais

- XI-B-3. Variables super globales utiles

- XI-B-4. Remplacer les variables super globales

- XII. Que faut-il éviter ?

- XII-A. Mots nus

- XII-A-1. Bons usages des mots nus

- XII-A-1-a. Mots nus comme clés de hachage

- XII-A-1-b. Mots nus comme noms de paquetage

- XII-A-1-c. Mots nus comme noms de bloc de code

- XII-A-1-d. Mots nus comme constantes

- XII-A-2. Utilisations malavisées des mots nus

- XII-A-2-a. Mots nus comme valeurs de hachage

- XII-A-2-b. Mots nus comme appels de fonction

- XII-A-2-c. Mots nus comme descripteurs de fichiers

- XII-A-2-d. Mots nus comme fonctions de tri

- XII-B. Objets indirects

- XII-B-1. Mots nus comme appels indirects

- XII-B-2. Limitations scalaires de la notation indirecte

- XII-B-3. Alternatives à la notation indirecte

- XII-C. Prototypes

- XII-C-1. Le problème avec les prototypes

- XII-C-2. Bons usages des prototypes

- XII-D. L'équivalence méthode-fonction

- XII-D-1. Côté appelant

- XII-D-2. Côté appelé

- XII-E. Déréférencement automatique

- XII-F. Variables liées

- XII-F-1. Lier des variables

- XII-F-2. Mise en œuvre des variables liées

- XII-F-3. Quand utiliser les variables liées ?

- XIII. Qu'est-ce qui manque ?

- XIII-A. Défauts manquants

- XIII-A-1. Le pragma strict

- XIII-A-2. Le pragma « warnings »

- XIII-A-3. IO::File et IO::Handle

- XIII-A-4. Le pragma « autodie »

- XIII-A-5. Les numéros de version Perl

- XIV. Remerciements Developpez

I - Préface

Perl Moderne (Modern Perl) est une façon de décrire la manière dont travaillent les programmeurs Perl les plus efficaces au monde. Ils utilisent les idiomes du langage. Ils tirent parti du CPAN. Ils ont le bon goût et l'habileté d'écrire du code puissant, maintenable, évolutif, concis et efficace. Vous aussi pouvez acquérir ces compétences !

Perl est apparu en 1987 comme un simple outil d'administration système. Bien qu'il ait commencé en occupant une niche confortable entre les scripts shell et la programmation en C, il est devenu une puissante famille de langages de programmation d'usage général. Perl a une histoire solide de pragmatisme et un avenir brillant de perfectionnement et d'amélioration.

Au cours de la longue histoire de Perl — en particulier les 20 années de Perl 5 — notre compréhension de ce qui fait de grands programmes en Perl a changé. Alors que vous pouvez écrire des programmes productifs sans jamais profiter de tout ce que le langage a à offrir, la communauté Perl mondiale a inventé, emprunté, amélioré et poli des idées, et les a rendues accessibles à quiconque est prêt à les apprendre.

I-A - Exécuter du Perl moderne

Le module Modern::Perl du CPAN (Le CPAN) demande à Perl de signaler les fautes de frappe et les constructions douteuses et permettra d'utiliser les nouvelles fonctionnalités introduites dans les versions récentes successives de Perl. Sauf mention contraire, les extraits de code présentés dans ce livre adoptent toujours le squelette de base d'un programme :

#!/usr/bin/env perl

use Modern::Perl '2013';
use autodie;

... ce qui équivaut à :

#!/usr/bin/env perl

use 5.016;      # implique "use strict;"
use warnings;
use autodie;

Quelques exemples utilisent des fonctions de test telles que ok(), like() et is()(Tests). Ces programmes suivent le modèle :

#!/usr/bin/env perl

use Modern::Perl;
use Test::More;

# exemple de code ici

done_testing();

Au moment où nous rédigeons ces lignes, la version stable actuelle est Perl 5.18 (1). Si vous utilisez une version de Perl plus ancienne, vous ne serez peut-être pas en mesure d'exécuter tous les exemples dans ce livre sans les modifier. Les exemples de ce livre fonctionnent mieux avec Perl 5.14.0 ou une version plus récente, mais nous recommandons au moins Perl 5.16. Bien que le terme « Perl moderne » puisse faire référence à n'importe quelle version de Perl à partir de 5.10.1, plusieurs fonctionnalités ajoutées dans les versions plus récentes sont indispensables au développement moderne.

Si vous n'avez aucune version de Perl installée (ou si vous disposez d'une ancienne version), vous pouvez installer vous-même une version plus récente. Les utilisateurs de Windows pourront télécharger Strawberry Perl à l'adresse http://www.strawberryperl.com/ ou ActivePerl à l'adresse http://www.activestate.com/activeperl. Les utilisateurs d'autres systèmes d'exploitation ayant déjà installé Perl (et un compilateur C et d'autres outils de développement) devraient commencer par installer le module CPAN App::perlbrew. Regardez sur http://search.cpan.org/perldoc?App::perlbrew pour les instructions d'installation.

Le module perlbrew vous permet d'installer et de gérer plusieurs versions de Perl. Cela vous permet de basculer entre versions ainsi que d'installer des modules Perl et CPAN dans votre répertoire personnel sans affecter la version du système. Si vous avez déjà eu besoin de demander à votre administrateur système la permission d'installer un logiciel, vous savez à quel point cela peut maintenant vous faciliter la vie.

I-B - Remerciements

Ce livre n'aurait pas existé sans les questions, les commentaires, les suggestions, les conseils, la sagesse et les encouragements de beaucoup de gens. L'auteur et l'éditeur remercient en particulier :

John SJ Anderson, Peter Aronoff, Lee Aylward, Alex Balhatchet, Nitesh Bezzala, Arnfjord Bjarmason, Matthias Bloch, John Bokma, Vasily Chekalkin, Dmitry Chestnykh, E. Choroba, Tom Christiansen, Anneli Cuss, Paulo Custodio, Steve Dickinson, Kurt Edmiston , Felipe, Shlomi poisson, Jérémie Foster, Mark Fowler, John Gabriele, Nathan Glenn, Kevin Granade, Andrew Grangaard, Bruce Gray, Bjørn Hansen Demandez, Tim Heaney, Graeme Hewson, Robert Hicks, Michael Hicks, Michael Hind, Mark Hindess, Yary Hluchan, Daniel Holz, Mike Huffman, Gary H. Jones II, Curtis Jewell, Mohammed Kamal Arafat, James E Keenan, Kirk Kimmel, Graham Knop, Yuval Kogman, Jan Krynicky, Michael Lang, Jeff Lavallee, Moritz Lenz, Andy Lester, Jean -Baptiste Mazon, Josh McAdams, Gareth McCaughan, John McNamara, Shawn M Moore, Alex Muntada, Carl masak, Chris Niswander, Nelo Onyiah, Chas. Owens, ww de PerlMonks, Jess Robinson, Dave Rolsky, Gabrielle Roth, Grzegorz Rożniecki, Jean-Pierre Rupp, Eduardo Santiago, Andrew Savige, Lorne Schachter, Steve Schulze, Dan Scott, Alex-ander Scott-Johns, Phillip Smith, Christopher E . Stith, Mark A. Stratman, Bryan Summersett, Audrey Tang, Scott Thomson, Ben Tilly, Ruud HG van Tol, Sam Vilain, Larry Wall, Lewis Wall, Paul Waring, Colin Wetherbee, Frank Wiegand, Doug Wilson, Sawyer X, David Yingling, Marko Zagozen, Ahmad M. Zawawi, harleypig, HBM, et sunnavy.

Les erreurs restantes sont de la faute de l'auteur têtu.

II - La philosophie Perl

Perl est efficace  il est souple, indulgent et malléable. Des programmeurs expérimentés l'utilisent chaque jour pour tout, depuis des scripts unilignes à usage unique permettant d'automatiser rapidement une petite tâche jusqu'à des projets pluriannuels et multiprogrammeurs.

Perl est pragmatique. Vous détenez le contrôle. Vous décidez comment résoudre vos problèmes et Perl s'adaptera pour faire ce que vous désirez, avec peu de frustration et sans aucune cérémonie.

Perl grandira avec vous. Dans l'heure qui suit, vous allez en apprendre assez pour écrire des programmes réels, utiles et vous comprendrez comment fonctionne le langage et pourquoi il fonctionne ainsi. Modern Perl tire parti de cette connaissance et de l'expérience combinée de la communauté mondiale Perl, pour vous aider à écrire du code fonctionnel et maintenable.

Tout d'abord, vous devez savoir comment apprendre plus.

II-A - Perldoc

Perl possède une culture de la documentation utile. L'utilitaire perldoc fait partie de chaque installation Perl complète. Toutefois, votre système de type Unix peut nécessiter l'installation d'un paquetage supplémentaire comme perl-doc sur les versions Debian ou Ubuntu de GNU/Linux. L'utilitaire de ligne de commande perldoc affiche la documentation de chaque module Perl installé sur le système, que ce soit un module standard intégré à la distribution ou un module installé séparément à partir du CPAN (Comprehensive Perl Archive Network), ainsi que des milliers de pages de l'abondante documentation Perl de base.

http://perldoc.perl.org/ héberge les versions récentes de la documentation Perl. Les index CPAN à http://search.cpan.org/ et http://metacpan.org/ fournissent de la documentation pour tous les modules du CPAN. Les autres distributions telles que ActivePerl et Strawberry Perl fournissent une documentation locale en formats HTML.

Utilisez perldoc pour lire la documentation d'un module ou une partie de la documentation de base :

$ perldoc List::Util
$ perldoc perltoc
$ perldoc Moose::Manual

Le premier exemple affiche la documentation intégrée dans le module List::Util. Le deuxième exemple affiche un fichier de documentation pure, en l'occurrence la table des matières de la documentation principale. Le troisième exemple affiche un fichier de documentation pure inclus dans une distribution CPAN du module Moose. (Moose). perldoc cache ces détails ; il n'y a aucune différence entre la lecture de la documentation pour une bibliothèque standard comme Data::Dumper, ou une installée à partir du CPAN. Cette cohérence représente un avantage pour vous  la culture Perl valorise tellement la documentation que même les bibliothèques externes ont tendance à suivre le bon exemple de la documentation de base du langage.

Le modèle standard de documentation inclut une description du module, montre des exemples d'utilisation et puis contient une explication détaillée du module et de son interface. Bien que la quantité de documentation varie selon l'auteur, sa forme est remarquablement cohérente.

Comment lire la documentation

Perl dispose de beaucoup de documentation. Par où commencer ?
perldoc perltoc affiche la table des matières de la documentation principale et perldoc perlfaq affiche la table des matières de la Foire aux Questions sur Perl. perldoc perlop et perldoc perlsyn documentent les opérateurs symboliques et les constructions syntaxiques de Perl. perldoc perldiag explique la signification des messages d'avertissement de Perl. perldoc perlvar répertorie toutes les variables symboliques de Perl. Parcourir ces fichiers vous donnera un bon aperçu du langage.

L'utilitaire perldoc dispose de bien d'autres fonctionnalités (voir perldoc perldoc). Pour rechercher la FAQ Perl, utilisez l'option -q avec un mot clé. Par exemple, perldoc -q sort renvoie trois questions : Comment puis-je trier un tableau par (quelque chose) ?, Comment puis-je trier une table de hachage (éventuellement par valeur et non par clé) ? et Comment puis-je toujours garder mes tables de hachage triées ?.

L'option -f affiche la documentation pour une fonction interne de Perl. perldoc -f sort explique le comportement de l'opérateur sort. Si vous ne connaissez pas le nom de la fonction souhaitée, consultez la liste des fonctions intégrées disponibles dans perldoc perlfunc.

L'option -v recherche une variable interne. Par exemple, perldoc -v $PID affiche la documentation de la variable qui contient l'identifiant du processus du programme en cours. Selon votre shell, il se peut que vous deviez mettre la variable entre des guillemets ou apostrophes.

L'option -l permet à perldoc d'afficher le chemin d'accès au fichier de documentation plutôt que le contenu de la documentation. Sachez qu'un module peut avoir un fichier .pod distinct en plus de son fichier .pm.

L'option -m affiche l'intégralité du contenu du module, code et tout, sans effectuer aucune mise en forme spéciale.

Perl utilise un format de documentation appelé POD, qui est l'abréviation de Plain Old Documentation. perldoc perlpod décrit comment fonctionne POD. D'autres outils POD incluent podchecker, qui valide la structure des documents POD et le module CPAN Pod::Webserver, qui affiche des POD locales en format HTML via un serveur web minimal.

II-B - Expressivité

Larry Wall a étudié la linguistique et les langues humaines. Puis il a conçu Perl. Contrairement à d'autres langages conçus autour d'une notion mathématique, Perl prend en compte la façon dont les gens communiquent. En retour, vous obtenez la liberté de décider comment organiser vos programmes pour répondre à vos besoins. Vous pouvez écrire du code simple et direct d'un seul tenant ou combiner plusieurs petits morceaux en programmes plus vastes. Vous pouvez choisir parmi les multiples modèles de programmation et pouvez éviter ou accepter des fonctionnalités avancées.

Là où d'autres langages affirment qu'il devrait y avoir une seule meilleure façon de résoudre un problème, Perl vous permet de décider ce qui est plus lisible, plus utile, plus attrayant ou plus amusant.

Les passionnés de Perl ont un slogan pour cela: TIMTOWTDI, prononcé « Tim Toady », ou « There's more than one way to do it ! » (2).

Bien que cette expressivité permette aux orfèvres de créer des programmes étonnants, elle permet également à l'imprudent de faire des dégâts. L'expérience et le bon goût vous guideront pendant la conception de votre code, mais le choix vous appartient toujours. Exprimez-vous, mais gardez à l'esprit la lisibilité et la maintenabilité, en particulier pour ceux qui viendront après vous.

Apprendre Perl ressemble à l'apprentissage d'une langue naturelle. Vous apprendrez quelques mots, puis enchaînerez des phrases et puis profiterez de conversations simples. La maîtrise vient de la pratique de la lecture et l'écriture du code. Vous n'avez pas à comprendre tous les détails de Perl pour être productif, mais les principes énoncés dans ce chapitre sont essentiels à votre croissance comme programmeur.

Les débutants en Perl trouvent souvent opaques certaines constructions syntaxiques. Ces idiomes (Expressions idiomatiques) offrent une puissance formidable (mais subtile) aux programmeurs expérimentés, mais vous pouvez parfaitement vous permettre de les éviter jusqu'à ce que vous soyez à l'aise avec eux.

Un autre objectif ayant présidé à la conception du langage a été d'essayer d'éviter de surprendre les programmeurs (Perl) expérimentés. Par exemple, l'ajout de deux variables ($first_num + $second_num) est évidemment une opération numérique (Opérateurs numériques) ; l'opérateur d'addition doit traiter les deux variables comme des valeurs numériques pour produire un résultat numérique. Peu importe le contenu de $first_num et $second_num, Perl les contraindra à des valeurs numériques (Coercition numérique). Vous avez exprimé votre intention de les traiter comme des nombres en utilisant un opérateur numérique. Perl le fait volontiers.

Les adeptes de Perl appellent souvent ce principe DWIM, ou do what I mean 3« Fais ce que je veux dire. ». Vous pourriez aussi bien dire que Perl suit le principe de moindre surprise. Même avec une compréhension rudimentaire de Perl (notamment le Contexte), il devrait être possible de comprendre l'intention d'une expression Perl inconnue. Vous allez développer cette compétence pendant l'apprentissage du Perl.

L'expressivité du Perl permet aux débutants d'écrire des programmes utiles sans avoir à comprendre tout le langage. Perl a été conçu pour cela ! Les développeurs expérimentés appellent souvent le résultat du Baby Perl (« Perl du bébé »). Il s'agit d'un terme affectueux, car tout le monde commence un jour comme débutant. Par la pratique et en apprenant des programmeurs plus expérimentés, vous comprendrez et adopterez des idiomes et techniques plus puissants. Il est normal d'écrire un code simple que vous comprenez. Continuez à pratiquer et Perl deviendra en quelque sorte votre seconde langue maternelle.

Un expert en Perl pourrait tripler les nombres d'une liste en utilisant :

my @triples = map { $_ * 3 } @nombres;

... et un adepte du Perl pourrait écrire :

my @triples;

for my $nombre (@nombres)
{
push @triples, $nombre * 3;
}

... alors qu'un débutant pourrait essayer :

my @triples;

for (my $i = 0; $i < scalar @nombres; $i++)
{
$triples[$i] = $nombres[$i] * 3;
}

Chacun de ces programmes fait la même chose, mais ils utilisent Perl d'une manière différente.

En devenant plus à l'aise avec Perl, vous pouvez demander au langage d'en faire plus pour vous. Avec l'expérience, vous pouvez vous concentrer sur ce que vous voulez faire, plutôt que comment faire. Pourtant, Perl exécutera volontiers tant le Baby Perl que le Perl de l'expert. Vous pouvez concevoir et affiner vos programmes pour plus de clarté, expressivité, réutilisation et maintenabilité, en partie ou en totalité. Profitez de cette souplesse et du pragmatisme : c'est beaucoup mieux d'accomplir efficacement votre tâche maintenant que d'écrire un programme conceptuel pur et beau l'année prochaine.

II-C - Contexte

Dans les langues parlées, la signification d'un mot ou d'une phrase peut dépendre de la façon dont vous l'utilisez ; le contexte local permet de clarifier l'intention. Par exemple, la pluralisation inappropriée dans « S'il vous plaît donnez-moi un hamburgers ! ». La pluralisation du sujet diffère de la quantité et sonne faux, tout comme le genre incorrect de « la chat ». L'article est féminin, mais le substantif est masculin et cela fait sourire les francophones de naissance. D'autres mots ont des exceptions bizarres. Il est (ou était) par exemple admis que les mots « orgue », « délice » et « amour » étaient masculins au singulier et féminins au pluriel (la règle est moins simpliste que cela, mais on s'en contentera ici). Ce qui a permis par exemple à un humoriste d'écrire : «  Cet orgue est le plus beau de toutes celles que j'ai entendues ».

Le contexte en Perl est similaire. Il décrit la quantité ainsi que le type de données à utiliser. Perl va faire ce que vous entendez faire des données si vous choisissez le contexte approprié pour ces données.

Par exemple, plusieurs opérations en Perl produisent des comportements différents selon que vous attendez zéro, un, ou plusieurs résultats. Une construction spécifique en Perl peut faire quelque chose de différent si vous écrivez, « Fais ceci, mais je ne me soucie pas du résultat » par rapport à « Fais ceci et donne-moi plusieurs résultats ». D'autres opérations vous permettent de spécifier si vous souhaitez travailler avec des données numériques, textuelles ou booléennes.

Le contexte peut être difficile si vous essayez d'écrire ou de lire du code Perl comme une série d'étapes simples isolées. Ce n'est pas comme cela que Perl fonctionne ! Chaque expression fait partie d'un contexte plus large. Vous pourriez vous taper le front après une longue session de débogage lorsque vous découvrez que vos hypothèses sur le contexte étaient incorrectes. Si au contraire vous êtes au courant du contexte, votre code sera plus correct et plus propre, souple, et plus concis.

II-C-1 - Contexte vide, scalaire et de liste

Le contexte de quantité définit combien d'éléments vous attendez d'une opération. L'accord en nombre sujet-prédicat de la langue française représente un parallèle proche. Même sans connaître la description formelle de ce principe linguistique, vous comprenez sans doute l'erreur dans la phrase « Perl sont un langage amusant ». En ce qui concerne le contexte de quantité, on peut dire que le verbe « sont » attend un pluriel. En Perl, le nombre d'éléments que vous demandez détermine combien vous en obtenez.

Supposons que la fonction (Déclarer des fonctions) trouver_corvees() trie votre liste de tâches ménagères par ordre de priorité. Le nombre de tâches que vous vous attendez à lire à partir de votre liste détermine exactement ce que la fonction va faire. Si vous ne vous attendez à rien, vous faites juste semblant d'être occupé. Si vous vous attendez à une tâche, vous aurez quelque chose à faire pour le prochain quart d'heure. Si vous avez un sursaut d'énergie sur un week-end, vous pouvez obtenir toutes les tâches.

Lorsque vous appelez une fonction et n'utilisez jamais sa valeur de retour, vous utilisez un contexte void :

trouver_corvees();

Affecter la valeur de retour de la fonction à un seul élément (Scalaires) met en œuvre le contexte scalaire :

my $resultat_unique = trouver_corvees();

L'affectation des résultats de l'appel de la fonction à un tableau (Tableaux) ou à une liste, ou leur utilisation dans une liste évalue la fonction dans un contexte de liste :

my @tous_les_resultats        = trouver_corvees();
my ($premier_element, @reste) = trouver_corvees();

# liste des résultats passés à une fonction
traiter_liste_de_resultats( trouver_corvees () );

Les parenthèses sur la deuxième ligne de l'exemple précédent groupent les deux déclarations de variables (Portée lexicale) en une seule, afin que l'affectation initialise les deux variables à la fois. Notez que, cependant, une liste contenant un seul objet est toujours une liste. Vous pouvez aussi bien écrire :

my ($element_unique)   = find_chores();

... auquel cas les parenthèses indiquent à l'analyseur Perl que vous voulez utiliser un contexte de liste pour l'initialisation, même si vous attribuez un seul élément d'une liste. C'est subtil, mais maintenant que vous êtes au courant, la différence de contexte de quantité entre ces deux déclarations devrait être évidente :

my $contexte_scalaire   = trouver_corvees();
my ($contexte_de_liste) = trouver_corvees();

L'évaluation d'une fonction ou d'une expression  à l'exception de l'affectation  dans un contexte de liste peut générer de la confusion. Les listes propagent le contexte de liste aux expressions qu'elles contiennent. Les deux appels à trouver_corvees() se produisent dans un contexte de liste :

traiter_liste_de_resultats( trouver_corvees() );

my %resultats =
(
operation_economique => $resultats_economiques,
operation_couteuse   => trouver_corvees(), # OUPS!
);

Le dernier exemple surprend souvent les programmeurs débutants, car l'initialisation d'une table de hachage (Tables de hachage) avec une liste de valeurs impose à trouver_corvees un contexte de liste. Utilisez l'opérateur scalar pour imposer un contexte scalaire :

my %resultats =
(
operation_economique => $resultats_economiques,
operation_couteuse   => scalar trouver_corvees(),
);

Pourquoi le contexte est-il important ? Une fonction sensible au contexte peut examiner son contexte d'appel et décider la quantité de travail qu'elle doit faire. Dans un contexte vide, find_chores() peut légitimement ne rien faire. Dans un contexte scalaire, elle peut retrouver juste la tâche la plus importante. Dans un contexte de liste, elle doit trier (opération peut-être chère) et retourner la liste complète triée.

II-C-2 - Contextes numériques, de chaîne et booléen

L'autre contexte de Perl  le contexte de valeur  détermine la façon dont Perl interprète un fragment des données. Vous avez probablement déjà remarqué que Perl peut déduire si vous avez un nombre ou une chaîne de caractères et convertir des données entre les deux formats. En contrepartie de l'absence de nécessité de déclarer (ou au moins de suivre) explicitement le type de données contenu dans une variable ou produit par une fonction, les contextes de valeurs de Perl fournissent des indices sur la façon de traiter ces données.

Perl contraindra les valeurs aux types spécifiques appropriés (Coercition), selon les opérateurs que vous utilisez. Par exemple, l'opérateur eq teste si les chaînes de caractères contiennent la même information sous forme de chaîne :

say "Désastre cryptographique!" if $alice eq $bob;

Vous avez peut-être eu une expérience déroutante où vous saviez que les chaînes étaient différentes, mais semblaient identiques lors de la comparaison :

my $alice = 'alice';
say "Désastre cryptographique!" if $alice == 'Bob';

Alors que l'opérateur eq traite ses opérandes comme des chaînes en leur appliquant le contexte de chaîne de caractères, l'opérateur == impose un contexte numérique. En contexte numérique, les deux chaînes sont évaluées à 0 (Coercition numérique). Assurez-vous d'utiliser l'opérateur approprié pour le type de contexte souhaité.

Le contexte booléen survient lorsque vous utilisez une valeur dans une instruction conditionnelle. Dans les exemples précédents, if évaluait les résultats des opérateurs eq et == dans un contexte booléen.

Dans de rares cas, vous devrez peut-être forcer un contexte explicite là où il n'existe aucun opérateur typé approprié. Pour forcer un contexte numérique, ajoutez zéro à une variable. Pour forcer un contexte de chaîne de caractères, concaténez une variable à une chaîne vide. Pour forcer un contexte booléen, doublez l'opérateur de négation :

my $numeric_x =  0 + $x;  # force le contexte numérique
my $stringy_x = '' . $x;  # force le contexte de chaîne de caractères
my $boolean_x =    !!$x;  # force le contexte booléen

Les contextes de valeur sont plus faciles à identifier que les contextes de quantité. Une fois que vous savez quels opérateurs fournissent quels contextes (Types d'opérateurs), vous ferez rarement des erreurs.

II-D - Idées implicites

Les programmeurs qui connaissent les raccourcis linguistiques de Perl peuvent regarder un bout de code et comprendre instantanément ses caractéristiques les plus importantes. En plus des contextes, Perl possède des variables par défaut  l'équivalent en programmation des pronoms.

II-D-1 - La variable scalaire par défaut

La variable scalaire par défaut (également appelée topic variable ou variable sujet), $_, est surtout remarquable en son absence : beaucoup d'opérations internes à Perl travaillent sur le contenu de $_ en l'absence d'une variable explicite. Vous pouvez toujours utiliser $_ comme variable, mais c'est souvent inutile.

Beaucoup d'opérateurs internes de Perl (y compris chomp , split chr, ord, lc, length, reverse et uc) travaillent sur la variable scalaire par défaut si vous ne leur fournissez pas une alternative. Par exemple, la commande interne chomp supprime toute séquence de saut de ligne de la fin de son opérande. Voir perldoc -f chomp et $/ pour des détails plus précis sur son comportement.

my $oncle = "Bob\n";
chomp $oncle;
say "'$oncle'";

$_ a en Perl la même fonction que le pronom ceci en français. Sans une variable explicite, chomp supprime la séquence finale de saut de ligne de $_. Perl comprend ce que vous voulez dire quand vous dites « chomp » : Perl va toujours chomper ceci. Ces deux lignes de code sont équivalentes :

chomp $_;
chomp;

De même, say et print fonctionnent sur $_ en l'absence d'autres arguments :

print;  # affiche $_ dans le descripteur de fichier courant
say;    # affiche "$_\n" dans le descripteur de fichier courant

Les expressions régulières du Perl (Expressions régulières et correspondance) utilisent par défaut $_ pour la correspondance, la substitution et la translittération :

$_ = 'Mon nom est Paquito';
say if /Mon nom est/;

s/Paquito/Paquita/;

tr/A-Z/a-z/;
say;

Les instructions de boucle du Perl (Boucles) utilisent par défaut $_ comme variable d'itération. Considérons une boucle for sur une liste :

say "#$_" for 1 .. 10;

for (1 .. 10)
{
say "#$_";
}

... ou une boucle while :

while (<STDIN>)
{
chomp;
say scalar reverse;
}

… ou la transformation d'une liste avec map :

my @carres = map { $_ * $_ } 1 .. 10;
say for @carres;

… ou le filtrage d'une liste avec grep :

say 'L'heure du brunch !'
if grep { /pâte à crêpes/ } @garde_manger;

De même que le français (ou l'anglais) peut devenir confus lorsque vous avez trop de pronoms et d'antécédents, il peut arriver la même chose en Perl lorsque vous mélangez des usages explicites et implicites de $_. Si vous l'utilisez dans plusieurs endroits, un morceau de code peut substituer silencieusement la valeur attendue par un autre morceau de code. Par exemple, si une fonction utilise $_ et que vous l'appelez dans une autre fonction qui utilise $_, a valeur 'appelée peut écraser la valeur de l'appelante :

while (<STDIN>)
{
chomp;

# MAUVAIS EXEMPLE
my $modif = calcule_valeur( $_ );
say "Original: $_";
say "Modifié  : $modif";
}

Si calcule_valeur() ou toute autre fonction a modifié $_, ce changement persisterait à travers cette itération de la boucle. À partir de Perl 5.10, vous pouvez déclarer $_ comme une variable lexicale (Portée lexicale) avec my pour empêcher l'écrasement d'une instance existante de $_ :

while (my $_ = <STDIN>)
{
...
}

Malheureusement, cette construction a quelques cas limites liés à la façon dont se comportent les fonctions existantes quand elles s'attendent à ce que $_ soit une variable globale. À partir de Perl 5.18, les Perl 5 Porters considèrent cette fonctionnalité comme expérimentale. Utilisez-la avec prudence. En outre, l'utilisation d'une variable lexicale nommée peut être encore plus claire :

while (my $line = <STDIN>)
{
...
}

Utilisez $_ comme le mot « il » dans l'écriture formelle : avec parcimonie, dans de petits cadres bien définis.

L'opérateur ...

Perl 5.12 introduit l'opérateur triple-point (...) comme un espace réservé pour du code que vous souhaitez compléter plus tard. Perl l'analysera comme une instruction complète à la compilation, mais déclenchera une exception comme quoi vous essayez d'exécuter du code non implémenté si vous tentez de l'exécuter. Voir perldoc perlop pour plus de détails.

II-D-2 - Les variables tableau par défaut

Perl fournit également deux variables implicites de type tableau. Perl passe des arguments aux fonctions (Déclarer des fonctions) dans un tableau nommé @_. Les opérations de manipulation de tableau (Tableaux) à l'intérieur des fonctions ont lieu sur ce tableau par défaut. Ces deux extraits de code sont équivalents :

sub toto
{
my $arg = shift;
...
}

sub toto_explicites_args
{
my $arg = shift @_;
...
}

Tout comme $_ correspond au pronom il, @_ correspond aux pronoms ils et eux. Contrairement à $_, Perl localise automatiquement @_ pour vous lorsque vous appelez d'autres fonctions. Les commandes internes shift et pop prennent pour opérande @_, si aucun opérande explicite n'est fourni.

En dehors de toute fonction, la variable tableau par défaut @ARGV contient les arguments de ligne de commande pour le programme. Les opérations sur tableaux en Perl (y compris shift et pop) utilisent implicitement @ARGV en dehors des fonctions. Vous ne pouvez pas utiliser @_ à la place de @ARGV.

L'opérateur readline

L'opérateur <$fh> de Perl est identique à la commande interne readline. readline $fh fait la même chose que <$fh>. Comme à partir de Perl 5.10, une readline basique se comporte exactement comme <>, vous pouvez maintenant utiliser readline partout. Pour des raisons historiques, <> est encore plus fréquent, mais envisagez d'utiliser readline comme une alternative plus lisible. (Qu'est-ce qui est le plus lisible, glob '*.html' ou <*.html>? La même idée s'applique.)

ARGV a un cas particulier. Si vous lisez à partir du descripteur de fichier vide <>, Perl traitera chaque élément de @ARGV comme le nom d'un fichier à ouvrir en lecture. (Si @ARGV est vide, Perl lira depuis l'entrée standard.) Ce comportement implicite de @ARGV est utile pour écrire des programmes courts, tels qu'un filtre de ligne de commande qui inverse son entrée :

while (<>)
{
chomp;
say scalar reverse;
}

Pourquoi scalar ? say impose le contexte de liste sur ses opérandes. reverse passe son contexte aux opérandes, en les traitant comme une liste dans un contexte de liste et comme une chaîne concaténée dans un contexte scalaire. Si le comportement de reverse vous semble confus, votre instinct a raison. Perl aurait sans doute dû distinguer les opérateurs « inverser une chaîne » et « inverser une liste ».

Si vous l'exécutez avec une liste de fichiers :

$ perl reverse_lines.pl encrypted/*.txt

… le résultat sera un long flux de sortie. Sans arguments, vous pouvez fournir votre propre entrée standard en la redirigeant (avec un « pipe ») à partir d'un autre programme ou en tapant directement. C'est une grande flexibilité dans un petit programme, mais Perl ne fait que commencer.

III - Perl et sa communauté

Le plus grand succès de Perl est l'énorme quantité de bibliothèques réutilisables développées pour lui. Là où Perl 4 avait des bifurcations de code pour se connecter à des bases de données telles qu'Oracle et Sybase, par exemple, Perl dispose maintenant d'un véritable mécanisme d'extension. Larry voulait que les gens créent et maintiennent leurs propres extensions sans fragmenter Perl en des milliers de patois incompatibles  et cela a fonctionné.

Cette réussite technique était presque aussi importante que la croissance d'une communauté autour de Perl. Les gens écrivent des bibliothèques. Les gens continuent le travail d'autres personnes. Les gens rendent une communauté intéressante à joindre, à préserver et à développer.

La communauté Perl est forte et saine. Elle accueille des participants enthousiastes à tous les niveaux, de débutants aux développeurs experts. Profitez des connaissances et de l'expérience de nombreux autres programmeurs Perl, et vous deviendrez un meilleur programmeur.

III-A - Le CPAN

Perl est un langage pragmatique et la disponibilité de dizaines de milliers de bibliothèques Perl librement réutilisables démontre ce pragmatisme. Si vous avez un problème à résoudre, il y a de très bonnes chances que la communauté mondiale Perl ait déjà écrit et partagé le code pour le résoudre.

La programmation en Perl moderne utilise intensivement le CPAN (http://www.cpan.org/). Le Comprehensive Perl Archive Network (3) est un système de téléchargement et mise en miroir du code Perl redistribuable et réutilisable. C'est l'une des plus grandes archives de bibliothèques de code dans le monde, sinon la plus grande. Le CPAN offre des bibliothèques pour tout, des accès aux bases de données aux outils de profilage, à des protocoles pour presque tous les périphériques réseau jamais créés, à des bibliothèques de sons et graphiques et des fonctions wrappers pour appeler les bibliothèques partagées sur votre système.

Sans le CPAN, Modern Perl est juste un autre langage. Avec le CPAN, Modern Perl est un outil puissant pour résoudre des problèmes.

Le CPAN met en miroir des distributions, c'est-à-dire des collections de code Perl réutilisables. Une distribution peut contenir un ou plusieurs modules ou des bibliothèques autonomes de code Perl. Chaque distribution occupe son propre espace de noms dans le CPAN et fournit des métadonnées uniques.

Le CPAN est grand, très grand

Chaque mois des centaines de contributeurs enregistrés et des milliers de modules indexés dans des centaines de distributions s'ajoutent au CPAN. Ces chiffres ne prennent pas en compte les mises à jour. En janvier 2014, search.cpan.org a rapporté 11 021 contributeurs, 129 039 modules et 29 068 distributions (représentant un taux de croissance de 15,1 %, 21,2 %, et respectivement 18,1 % depuis l'édition précédente de ce livre).

Le CPAN lui-même est seulement un service de mise en miroir. Les auteurs téléchargent sur le serveur les distributions qui sont envoyées à différents sites miroirs à partir desquels les clients du CPAN les téléchargent, configurent, compilent, testent et installent. Cette simplicité a bien servi Perl, en permettant les contributions de milliers de bénévoles. En particulier, les normes communautaires ont évolué pour identifier les attributs et caractéristiques des distributions CPAN bien formées. Celles-ci comprennent :

- le comportement des installateurs CPAN automatisés ;

- des métadonnées pour décrire ce que chaque distribution offre et attend ;

- de la documentation et des licences lisibles par la machine.

Des services supplémentaires du CPAN fournissent des tests complets automatisés et des rapports afin d'améliorer la qualité du packaging et l'exactitude à travers les plates-formes et les versions de Perl. Chaque distribution CPAN a sa propre file d'attente à base de tickets sur http://rt.cpan.org/ pour signaler les bogues et travailler avec les auteurs. Les sites du CPAN affichent des liens vers les versions précédentes des distributions, les évaluations des modules, les annotations de la documentation, et plus encore. Tout cela est disponible à la fois sur http://search.cpan.org/ et http://metacpan.org/.

Les installations de Perl Moderne incluent un client pour se connecter, rechercher, télécharger, construire, tester et installer des distributions CPAN ; il s'agit de CPAN.pm. Avec une version récente (à ce jour, la dernière version stable est la 2.0), l'installation du module est relativement facile. Démarrez le client avec :

$ cpan

Pour installer une distribution depuis la ligne de commande du client :

$ cpan
cpan[1]> install Modern::Perl

... ou l'installer directement à la ligne de commande du système :

$ cpan Modern::Perl

Le tutoriel d'Eric Wilhelm sur la configuration de CPAN.pm http://learnperl.scratchcomputing.com/tutorials/configuration/ contient une excellente section de dépannage.

Même si le client CPAN est un module de base pour la distribution Perl, vous aurez probablement besoin d'installer des outils standard de développement, comme l'utilitaire make et éventuellement un compilateur C. Les utilisateurs de Windows devront regarder de côté de Strawberry Perl (http://strawberryperl.com/) et Strawberry Perl professionnel. Les utilisateurs de Mac OS X doivent installer XCode. Les utilisateurs d'Unix ou de Linux disposent souvent de ces outils (mais les utilisateurs de Debian et Ubuntu devront installer build-essential).

III-A-1 - Outils de gestion CPAN

Si votre système d'exploitation fournit sa propre installation de Perl, celle-ci peut être obsolète ou peut avoir ses propres dépendances envers des versions spécifiques de distributions du CPAN. Les développeurs Perl prudents construisent souvent des murs virtuels entre le Perl du système et leurs installations Perl de développement. Plusieurs projets contribuent à rendre cela possible.

App::cpanminus est un client CPAN plus récent ayant pour objectifs la vitesse, la simplicité et zéro configuration. Installez-le avec cpan App::cpanminus, ou :

$ curl -LO http://xrl.us/cpanm
$ less cpanm # revoir le code avant l'exécution
$ chmod +x cpanm
$ ./cpanm

App::perlbrew est un système pour gérer et basculer entre vos propres installations de multiples versions et configurations de Perl. L'installation est aussi simple que :

$ curl -LO http://xrl.us/perlbrew
$ less perlbrew # revoir le code avant l'exécution
$ chmod +x perlbrew
$ ./perlbrew install
$ perldoc App::perlbrew

La distribution CPAN local::lib vous permet d'installer et de gérer les distributions dans votre propre répertoire utilisateur, plutôt que pour l'ensemble du système. C'est un moyen efficace de maintenir les distributions CPAN pour des utilisateurs individuels sans affecter le système dans son ensemble. L'installation est un peu plus compliquée que pour les deux distributions précédentes, mais App::local::lib::helper peut simplifier le processus. Voir https://metacpan.org/pod/local::lib et https://metacpan.org/pod/App::local::lib::helper pour plus de détails.

Ces trois projets ont tous les trois tendance à supposer un environnement de type Unix (comme une distribution GNU/Linux ou même Mac OS X). Les utilisateurs de Windows peuvent télécharger Padre tout-en-un (http://padre.perlide.org/download.html).

III-B - Sites de la communauté

La page d'accueil de Perl, http://www.perl.org/ affiche des liens vers la documentation, le code source, des tutoriels, listes de diffusion et plusieurs projets Perl d'importance communautaire. Si vous débutez, la liste de diffusion pour les débutants en Perl est un endroit convivial pour poser des questions de novice et obtenir des réponses précises et utiles. Voir http://learn.perl.org/faq/beginners.html.

La page d'accueil du développement de Perl est http://dev.perl.org/, qui a des liens vers des ressources pertinentes pour le développement de Perl lui-même.

Perl.com publie des articles et des tutoriels sur Perl et sa culture. Ses archives remontent au XXe siècle. Voir http://www.perl.com/.

La page centrale du CPAN (Le CPAN) est http://www.cpan.org/, même si les utilisateurs expérimentés passent plus de temps sur http://search.cpan.org/. Cette plate-forme centrale de distribution de code Perl réutilisable et libre représente une partie essentielle de la communauté Perl. MetaCPAN (https://metacpan.org/) est une interface récente alternative au CPAN.

PerlMonks, à http://perlmonks.org/, est un site communautaire consacré aux discussions sur la programmation en Perl. Son histoire de plus d'une décennie en fait l'un des sites de questions et réponses les plus vénérables de tous les langages de programmation.

Plusieurs sites communautaires offrent des nouvelles et des commentaires. http://blogs.perl.org/ est une plate-forme de blog gratuite ouverte à tout membre de la communauté Perl.

D'autres sites regroupent les réflexions des experts Perl : http://perlsphere.net/, http://planet.perl.org/, et http://ironman.enlightenedperl.org/. Ce dernier fait partie d'une initiative de l'Organisation Éclairée Perl (http://enlightenedperl.org/) pour augmenter la quantité et améliorer la qualité des publications Perl sur le web.

Perl Buzz (http://perlbuzz.com/) recueille et republie sur une base régulière certaines des nouvelles les plus intéressantes et utiles au sujet de Perl. L'hebdomadaire Perl Weekly (http://perlweekly.com/) offre une prise hebdomadaire sur l'actualité du monde Perl.

III-C - Sites de développement

Le site Meilleures Solutions Pratiques (http://bestpractical.com/) maintient un système populaire de suivi de demandes, RT, pour auteurs CPAN ainsi que pour développement Perl. Chaque distribution CPAN a sa propre file d'attente RT sur http://rt.cpan.org/. Perl lui-même a une file d'attente à base de tickets à http://rt.perl.org/.

La liste de diffusion Perl 5 Porters (ou p5p) est le point focal du développement de Perl. Voir http://lists.cpan.org/showlist.cgi?name=perl5-porters.

La Fondation Perl (http://www.perlfoundation.org/) héberge un wiki pour toutes sortes de choses relatives à Perl. Voir http://www.perlfoundation.org/perl5.

Nombreux experts Perl utilisent Github (http://github.com/) pour héberger leurs projets... y compris les sources de ce livre à http://github.com/chromatic/modern_perl_book/. Voir en particulier Gitpan (http://github.com/gitpan/), qui accueille des dépôts Git retraçant l'histoire complète de toutes les distributions sur le CPAN.

Un miroir local de Git

GitPAN reçoit des mises à jour peu fréquentes. Plutôt que d'aller chercher des distributions CPAN sur GitPAN, pensez à utiliser le merveilleux module Git::CPAN::Patch de Yanick Champoux pour créer des dépôts Git locaux des distributions du CPAN.

III-D - Événements

La communauté Perl organise d'innombrables conférences, ateliers, séminaires et réunions. En particulier YAPC  Yet Another Perl Conference, « Encore une nouvelle conférence Perl »  est un modèle de conférence locale réussie, à faible coût, qui se tient chaque année sur plusieurs continents. Voir http://yapc.org/.

Le wiki de la Fondation Perl répertorie d'autres événements à http://www.perlfoundation.org/perl5/index.cgi?perl_events.

Des centaines de groupes locaux de Mongueurs de Perl (Perl Mongers) se réunissent fréquemment pour des discussions techniques et interaction sociale. Voir http://www.pm.org/ et http://mongueurs.pm/ pour les mongueurs de Perl en langue française.

À titre d'information, le logiciel de gestion des conférences et des participants aux conférences YAPC a été l'œuvre de développeurs essentiellement français et norvégiens du groupe Perlmongers.

III-E - IRC

Lorsque les mongueurs et les autres membres de la communauté Perl ne peuvent pas se rencontrer en personne, beaucoup collaborent et discutent en ligne grâce au système de chat textuel connu comme IRC. La plupart des projets Perl les plus populaires et utiles ont leurs propres canaux IRC, tels que #moose et #catalyst.

Le serveur principal pour la communauté Perl est irc://irc.perl.org/. Les canaux notables incluent #perl-help, pour une aide générale sur la programmation en Perl, et #perl-qa, consacré aux tests et autres problèmes de qualité. Sachez que #perl est un canal à usage général pour discuter de tout ce que ses participants veulent discuter... et, par conséquent, il n'offre pas principalement une assistance technique.

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ères) est activé, vous pouvez utiliser des caractères UTF-8 dans les identifiants. Les identifiants Perl suivants sont tous valides :

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 :

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 hachage) ou une structure de données imbriquées (Structures 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 (Scalaires) utilisent le symbole dollar ($). Les variables tableau (Tableaux) utilisent l'arobase (@). Les variables de hachage (Tables de hachage) utilisent le symbole de pourcentage (%) :

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 :

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 :

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 (Contexte) 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 :

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 (Paquetages). 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 (Pragmas), 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 (Valeurs). 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ée). La plupart des variables que vous rencontrerez ont une portée lexicale (Porté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 :

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 :

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 :

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

... ou une conversion dans une situation rvalue :

# 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é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 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 (Coercition) 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 (Contexte). 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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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é :

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 :

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.

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 :

my $paragraphe =<<'FIN_PARA';

Et Caïn répondit : "Non, il est toujours là."
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 :

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 (Coercition).

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 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 (Fichiers) 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 :

open my $fh, '<:utf8', $textfile;

my $unicode_string = <$fh>;

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

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é :

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 (Pragmas), 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 :

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 :

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 :

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.

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 ;

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 ;

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.

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 :

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 :

my $milliard = 1000000000;
my $milliard = 1_000_000_000;
my $milliard = 10_0_00_00_0_0_0;

En raison de la coercition (Coercition), 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 :

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 :

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) :

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 idiomatiques) :

my $count = () = get_clown_hats();

En raison de l'associativité à droite (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 :

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

… comme des cibles d'affectations :

my ($package, $filename, $line) = caller();

… ou comme des listes d'expressions :

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é)).

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

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 :

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) :

# 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 :

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 :

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 :

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 :

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 :

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 :

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 :

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ètres) :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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

done_testing();

La valeur de retour de ok() (Tests) 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 :

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 :

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 :

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 (Contexte). 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 (Surcharge) 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 :

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é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 :

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

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

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 :

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 :

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 :

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 :

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

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

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.

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

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

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 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 :

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 :

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 :

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 :

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.

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 :

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 :

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 :

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 :

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.

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 :

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 :

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

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

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 :

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 :

$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 :

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 :

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 :

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 # :

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 :

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 :

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é :

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 :

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 Matching). Pour écrire le jeu Caillou / papier / ciseaux  :

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 :

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é) 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 :

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 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 (Noms). Les variables scalaires utilisent toujours comme sigil le signe dollar ($) (Sigils 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 :

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 (Coercition). 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 :

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 :

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é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 :

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 :

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é) ([]) signifient toujours l'accès à un tableau par un indice.

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

# @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 :

# 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 :

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 :

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 :

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 :

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é). 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 :

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 :

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 :

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 :

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 :

# é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 :

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 :

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 (@) :

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 :

@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 :

# 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 :

# 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 :

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 :

# 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 à :

# 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 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 :

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 :

# 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 :

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é :

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 :

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 :

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 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 :

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 :

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 :

%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é) :

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 => :

# 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 :

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 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 :

# 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 (Coercition) 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 :

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 :

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) :

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 (Autovivification) au sein des structures de données imbriquées (Structures de données imbriquées).

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

$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 :

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

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

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

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

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 :

# 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 :

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

Ceci équivaut à l'initialisation :

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 :

my @client_adresses = @adresses{ @clients };

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

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.

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 :

# 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 :

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 :

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  :

{
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 :

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 :

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 :

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é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 (Dualvars), 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 :

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 (Autovivification) est utile lors de la manipulation des structures de données imbriquées (Structures de données imbriquées) :

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 :

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 :

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 :

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() (Importation), 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 :

package MyCode v1.2.1;

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

package Flipper::Expert v1969.3.7 { ... }

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

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 :

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 :

# 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 :

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 :

# 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 :

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 :

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 :

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 aliasing), de sorte que vous pouvez les modifier en place :

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 :

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é :

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) :

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 (->) :

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 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 tableaux) d'une référence de tableau :

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 :

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 :

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 :

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 :

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 % :

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 :

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 :

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

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

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 Fermetures). 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 :

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 :

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 :

$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 :

$cuire_quelque_chose_ref->( 'cupcakes' );

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

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 :

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 :

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 :

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 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 :

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 :

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é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 :

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 :

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 :

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 :

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 :

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 :

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é :

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) :

($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 :

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 :

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 (Pragmas) 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 :

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é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 :

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 :

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 (Moose) rendra votre code plus clair.

V - Opérateurs

Certaines personnes appellent Perl un « langage orienté opérateur ». Pour comprendre un programme Perl, vous devez comprendre comment les opérateurs interagissent avec leurs opérandes.

Un opérateur Perl est une série d'un ou plusieurs symboles utilisés dans le cadre de la syntaxe du langage. Chaque opérateur fonctionne sur zéro, un ou plusieurs opérandes. Considérez un opérateur comme une sorte particulière de fonction comprise par l'analyseur et ses opérandes comme des arguments.

V-A - Caractéristiques des opérateurs

Chaque opérateur possède plusieurs caractéristiques importantes qui régissent son comportement : le nombre d'opérandes sur lesquels il opère, les relations avec les autres opérateurs, les contextes qu'il applique et la syntaxe qu'il fournit.

perldoc perlop et perldoc perlsyn fournissent un grand volume d'informations sur les opérateurs Perl, mais la documentation suppose que vous connaissez déjà assez bien quelques concepts informatiques essentiels. Ces idées semblent compliquées, mais elles utilisent des noms complexes pour des concepts que vous connaissez déjà, même si vous n'y avez jamais réfléchi. Si vous pouvez faire des mathématiques élémentaires dans votre tête, tout ira bien.

V-A-1 - Ordre des opérations (priorité)

La priorité (ou précédence) d'un opérateur établit quand Perl doit l'évaluer dans une expression. L'ordre d'évaluation procède de la priorité la plus élevée vers la plus basse. Vous rappelez-vous les mathématiques de base ? Multiplier et diviser avant d'additionner ou soustraire. C'est la priorité. Parce que la priorité de la multiplication est supérieure à la priorité de l'addition, en Perl 7 + 7 * 10 est évalué à 77 et pas à 140.

Pour forcer l'évaluation de certains opérateurs avant les autres, groupez leurs sous-expressions entre parenthèses. En (7 + 7) * 10, le regroupement de l'addition en une seule unité oblige son évaluation avant la multiplication. Le résultat est 140.

perldoc perlop contient un tableau de priorités. Parcourez-le quelques fois, mais ne vous donnez pas la peine de le mémoriser (presque personne ne le fait). Passez votre temps à simplifier votre code où vous le pouvez et ajoutez des parenthèses pour clarification où vous en avez besoin.

Dans les cas où deux opérateurs ont la même priorité, d'autres facteurs tels que l'associativité (Associativité) et la fixité (Fixité) permettent de départager.

V-A-2 - Associativité

L'associativité d'un opérateur détermine s'il est évalué de gauche à droite ou de droite à gauche. L'addition est associative à gauche, de sorte que 2 + 3 + 4 évalue d'abord 2 + 3, puis ajoute 4 au résultat. L'exponentiation est associative à droite, de sorte que 2 ** 3 ** 4 évalue d'abord 3 ** 4, puis élève 2 à la puissance 81. Comme d'habitude, le regroupement entre parenthèses vous permet de changer l'ordre d'évaluation.

Si vous mémorisez uniquement la priorité et l'associativité des opérateurs mathématiques courants, cela suffira. Simplifiez votre code et vous n'aurez pas à mémoriser d'autres associativités.

Le module standard B::Deparse est un outil de débogage inestimable. Exécutez perl -MO=Deparse,-p sur un bout de code pour voir exactement comment Perl gère la priorité et l'associativité des opérateurs. L'option -p ajoute des parenthèses de regroupement supplémentaires qui précisent souvent l'ordre d'évaluation.

Méfiez-vous, car l'optimiseur de Perl simplifiera les opérations mathématiques qui utilisent des valeurs constantes. Pour contourner ce problème, utilisez à la place des variables nommées, comme dans $x ** $y ** $z.

V-A-3 - Arité

L'arité d'un opérateur est le nombre d'opérandes sur lesquels il opère. Un opérateur d'arité 0 opère sur zéro opérande. Un opérateur unaire opère sur un opérande. Un opérateur binaire opère sur deux opérandes. Un opérateur ternaire opère sur trois opérandes. Un opérateur de liste opère sur une liste d'opérandes.

Les opérateurs arithmétiques sont des opérateurs binaires et sont généralement associatifs à gauche. 2 + 3 - 4 évalue d'abord 2 + 3 ; l'addition et la soustraction ont la même priorité, mais elles sont associatives à gauche et binaires, donc l'ordre approprié d'évaluation applique l'opérateur le plus à gauche (+) aux deux opérandes le plus à gauche (2 et 3) avec l'opérateur le plus à gauche (+), puis applique l'opérateur le plus à droite (-) au résultat de la première opération et à l'opérande le plus à droite (4).

Les débutants en Perl confondent souvent l'interaction des opérateurs de listes  surtout les appels de fonctions  et les expressions imbriquées. Là où généralement les parenthèses aident, méfiez-vous de la complexité de l'analyse de ce code :

# code probablement bogué
say ( 1 + 2 + 3 ) * 4;

... qui affiche la valeur 6 et est évalué (probablement) dans son ensemble à 4 (la valeur de retour de say multipliée par 4). L'analyseur Perl interprète les parenthèses comme des opérateurs postcirconfixés (Fixité) désignant les arguments de say, pas comme des parenthèses circonfixées regroupant une expression pour modifier la priorité.

V-A-4 - Fixité

La fixité (ne mémorisez pas tous ces mots, retenez juste leur signification) d'un opérateur est sa position par rapport à ses opérandes :

- les opérateurs infixés apparaissent entre leurs opérandes. La plupart des opérateurs mathématiques sont des opérateurs infixés, comme l'opérateur de multiplication dans $longueur * $largeur ;

- les opérateurs préfixés précèdent leurs opérandes. Les opérateurs postfixés suivent leurs opérandes. Ces opérateurs ont tendance à être unaires, comme la négation mathématique (-$x), la négation booléenne (!$y) et l'incrémentation postfixée ($z++) ;

- Les opérateurs circonfixés entourent leurs opérandes, comme dans le cas du constructeur de hachage anonyme ({...}) et les opérateurs de citation (qq[...]) ;

- les opérateurs postcirconfixés suivent certains opérandes et en entourent d'autres, comme on le voit dans l'accès des éléments de hachages et tableaux ($hachage{$x} et $tableau[$y]).

V-B - Types d'opérateurs

Les opérateurs Perl fournissent à leurs opérandes des contextes de valeur (Contextes numériques, de chaîne et booléen). Pour choisir l'opérateur approprié, vous devez connaître le type des opérandes que vous fournissez ainsi que le type de résultat que vous vous attendez à recevoir.

V-B-1 - Opérateurs numériques

Les opérateurs numériques imposent des contextes numériques sur leurs opérandes. Ces opérateurs sont les opérateurs arithmétiques standard tels que l'addition (+), la soustraction (-), la multiplication (*), la division (/), l'exponentiation (**), le modulo (%), leurs variantes avec affectation (+=, -=, *=, /=, **= et %=) et les autodécrémentations postfixées et préfixées (--).

L'opérateur d'auto-incrémentation a un comportement spécial avec les chaînes de caractères (Opérateurs spéciaux).

Plusieurs opérateurs de comparaison imposent des contextes numériques sur leurs opérandes. Ce sont l'égalité numérique (==), l'inégalité numérique (!=), supérieur à (>), inférieur à (<), supérieur ou égal à (>=), inférieur ou égal à (<=), et l'opérateur de comparaison de tri (<=>).

V-B-2 - Opérateurs de chaîne de caractères

Les opérateurs de chaînes imposent des contextes de chaîne sur leurs opérandes. Ces opérateurs sont les opérateurs d'application (positive ou négative) d'expressions régulières (=~ et !~) et la concaténation (.).

Plusieurs opérateurs de comparaison imposent des contextes de chaîne sur leurs opérandes. Ce sont l'égalité des chaînes (eq), l'inégalité des chaînes (ne), supérieur à (gt), inférieur à (lt), supérieur ou égal à (ge), inférieur ou égal à (le) et l'opérateur de comparaison de tri de chaîne (cmp).

V-B-3 - Opérateurs logiques

L'opérateur ou-défini, //, teste si son opérande est défini. Contrairement à || qui teste si son opérande est vrai, // retourne une valeur vraie même si son opérande est évalué à un zéro numérique ou à une chaîne vide. Ceci est particulièrement utile pour attribuer des valeurs par défaut aux paramètres :

sub nom_animal
{
my $nom = shift // 'Fluffy';
...
}

L'opérateur conditionnel ternaire (?:) accepte trois opérandes. Il évalue le premier en contexte booléen et retourne le deuxième si le premier est vrai et le troisième sinon :

my $etat_vrai = $valeur ? 'true' : 'false';

Les opérateurs préfixés ! et not retournent les contraires logiques des valeurs booléennes de leurs opérandes. not est une version moins prioritaire de !.

L'opérateur xor est un opérateur infixé qui évalue ses opérandes à ou-exclusif.

V-B-4 - Opérateurs de bit

Les opérateurs de bit traitent leurs opérandes numériquement bit par bit. Ces opérations sont rares. Elles sont le décalage à gauche (<<), le décalage à droite (>>), et binaire (&), ou binaire (|) et xor binaire (^), ainsi que leur variante avec affectation (<<=, >>=, &=, |= et ^=).

V-B-5 - Opérateurs spéciaux

L'opérateur d'auto-incrémentation a un comportement spécial. Lorsqu'il est utilisé sur une valeur avec une composante numérique (Coercition de variables mises en cache), l'opérateur incrémente cette composante numérique. Si la valeur est évidemment une chaîne (et n'a aucune composante numérique), l'opérateur incrémente la valeur littérale de l'élément de sorte que a devient b, zz devient aaa, et a9 devient b0.

my $num = 1;
my $str = 'a';

$num++;
$str++;
is( $num,   2, 'auto-incrémentation numérique' );
is( $str, 'b', 'auto-incrémentation de chaîne' );

no warnings 'numeric';
$num += $str;$str++;

is( $num, 2, 'addition numérique avec $str'    );
is( $str, 1, '... donne à $str une partie numérique' );

L'opérateur de répétition (x) est un opérateur infixé avec un comportement complexe. Lors de l'évaluation dans un contexte de liste avec une liste comme premier opérande, il renvoie cette liste répétée le nombre de fois spécifié par son second opérande. Lors de l'évaluation dans un contexte de liste avec un scalaire comme premier opérande, il produit une chaîne constituée par la valeur littérale de son premier opérande concaténée à elle-même le nombre de fois spécifié par son second opérande.

En contexte scalaire, l'opérateur répète et concatène une chaîne :

my @sheherazade = ('nuits') x 1001;
my $calendrier  =  'nuits'  x 1001;
my $cal_longueur =  length $calendar;

is( @sheherazade,    1001, 'liste répétée' );
is( $cal_longueur,   1001 * length 'nuits',
'chaîne répétant le mot' );

my @shenoliste    =  'nuits'  x 1001;
my $calscalaire   = ('nuits') x 1001;

is( @shenoliste, 1, 'liste à un élément répétant le mot' );
is( length $calscalaire,
1001 * length 'nuits', 'encore une chaîne répétant le mot' );

L'opérateur infixé d'intervalle (..) crée une liste d'éléments dans un contexte de liste :

my @cartes = ( 2 .. 10, 'V', 'D', 'R', 'A' );

Il peut créer seulement des successions simples auto-incrémentées d'entiers ou de chaînes.

Dans un contexte booléen, l'opérateur d'intervalle devient l'opérateur flip-flop. Cet opérateur produit une valeur fausse jusqu'à ce que son opérande gauche soit vrai. Cette valeur reste vraie jusqu'à ce que l'opérande droit soit vrai, après quoi la valeur est à nouveau fausse jusqu'à ce que l'opérande gauche soit à nouveau vrai. Imaginez l'analyse du texte d'une lettre officielle :

while (/Bonjour, $user/ .. /Cordialement,/)
{
say "> $_";
}

L'opérateur virgule (,) est un opérateur infixé. Dans un contexte scalaire, il évalue son opérande gauche puis retourne la valeur produite par l'évaluation de son opérande droit. Dans un contexte de liste, il évalue les deux opérandes de gauche à droite.

L'opérateur grosse virgule (=>) fait la même chose que la virgule et met aussi automatiquement entre guillemets tout « mot nu » utilisé comme opérande gauche (Tables de hachage).

L'opérateur triple point se substitue à une instruction simple. Il est d'arité 0 et n'a ni priorité, ni associativité. Il est passé correctement à la compilation, mais lève une exception avec la chaîne Unimplemented s'il est exécuté. Cela en fait un excellent emplacement dans du code dont vous pensez qu'il ne sera jamais exécuté :

sub un_exemple {
# implémentez vous-même ceci
...
}

VI - Fonctions

Une fonction (ou sous-programme ou subroutine) en Perl est une unité de comportement discrète et encapsulée. Un programme est un ensemble de petites boîtes noires où l'interaction de ces fonctions détermine le flot d'exécution du programme. Une fonction peut avoir un nom. Elle peut consommer de l'information entrante. Elle peut produire de l'information sortante.

En Perl, les fonctions sont un mécanisme privilégié pour l'abstraction, l'encapsulation et la réutilisation de code.

VI-A - Déclarer des fonctions

Utilisez la commande interne sub pour déclarer une fonction :

sub salue_moi  { ... }

Maintenant, la fonction salue_moi() est disponible et peut être appelée partout ailleurs dans le programme.

Tout comme vous pouvez déclarer une variable lexicale, mais laisser sa valeur non définie, vous pouvez déclarer une fonction sans la définir. Une déclaration préalable indique à Perl d'enregistrer qu'une fonction nommée existe. Vous pouvez la définir plus tard :

sub salue_soleil;

VI-B - Appeler des fonctions

Utilisez des parenthèses postfixées (Fixité) pour l'appel d'une fonction nommée. Les arguments de la fonction peuvent être placés entre parenthèses :

salue_moi( 'Jack', 'Tuxie' );
salue_moi( 'Snowy' );
salue_moi();

Bien que ces parenthèses ne soient pas strictement nécessaires pour ces exemples, même avec strict activé, elles offrent de la clarté aux lecteurs humains ainsi qu'à l'analyseur de Perl. En cas de doute, gardez-les.

Les arguments de fonction peuvent être des expressions arbitraires, y compris des variables et des appels de fonction :

salue_moi( $nom);
salue_moi( @auteurs );
salue_moi( %editeurs );
salue_moi( get_lecteurs() );

… même si la façon par défaut dont Perl traite les paramètres surprend parfois les débutants.

VI-C - Paramètres de fonction

Une fonction reçoit ses paramètres dans un seul tableau, @_ (Les variables tableau par défaut). Lorsque vous appelez une fonction, Perl aplatit tous les arguments fournis en une liste unique. La fonction doit extraire les paramètres en variables ou opérer directement sur @_ :

sub salue_un
{
my ($nom) = @_;
say "Salut, $nom !";
}

sub salue_tous
{
say "Salut, $_ !" for @_;
}

@_ se comporte comme un tableau normal. La plupart des fonctions Perl extraient les paramètres avec shift ou utilisent l'affectation de liste, mais il est possible d'accéder aux éléments individuels par indice :

sub salue_un_shift
{
my $nom = shift;
say "Salut, $nom !";
}

sub salue_deux_affectation_liste
{
my ($heros, $copain) = @_;
say "Bon si ce n'est pas $heros et $copain. Bienvenue !";
}

sub salue_un_indice
{
my $nom = $_[0];
say "Salut, $nom !";

# ou moins clair
say "Salut, $_[0] !";
}

Vous pouvez également utiliser unshift, push, pop, splice et slice sur @_. Rappelez-vous que les fonctions internes de manipulation de tableaux utilisent @_ comme opérande par défaut à l'intérieur des fonctions, de sorte que my $nom = shift; fonctionne. Profitez de cet idiome.

L'affectation d'un paramètre scalaire à partir de @_ nécessite shift, l'accès par indice à @_, ou un contexte de liste à gauche de l'affectation (lvalue)  des parenthèses. Autrement, Perl évaluera joyeusement @_ en contexte scalaire et affectera le nombre de paramètres transmis :

sub mauvais_salue_un
{
my $nom = @_;  # bogue
say "Salut, $nom; tu sembles numérique aujourd'hui !"
}

L'affectation d'une liste de paramètres est souvent plus claire que plusieurs lignes de shift. Comparez :

my $valeur_de_gauche = shift;
my $operation        = shift;
my $valeur_de_droite = shift;

... à :

my ($valeur_de_gauche, $operation, $valeur_de_droite) = @_;

Ce dernier exemple est plus simple à lire et est même légèrement plus rapide (bien que sa lisibilité améliorée soit beaucoup plus importante que la performance).

Il est parfois nécessaire d'extraire certains paramètres de @_ et de passer le reste à une autre fonction :

sub methode_deleguee
{
my $self = shift;
say 'Appeler methode_deleguee()'

$self->delegate->methode_deleguee( @_ );
}

Utilisez shift lorsque votre fonction a besoin d'un seul paramètre. Utilisez l'affectation de liste lors de l'accès à plusieurs paramètres.

Signatures réelles de fonction

Plusieurs distributions du CPAN étendent la manipulation de paramètres de Perl avec une syntaxe et des options supplémentaires. signatures et Method::Signatures sont puissantes. Method::Signatures::Simple est basique, mais utile. MooseX::Method::Signatures fonctionne très bien avec Moose (Moose). Function::Parameters mérite d'être explorée. Moops va beaucoup plus loin pour ajouter la syntaxe OO, mais contient aussi de bonnes signatures de fonctions.

VI-C-1 - Aplatissement

L'aplatissement de liste en @_ se passe du côté appelant d'un appel de fonction. Le passage d'un hachage comme argument produit une liste de paires clé/valeur :

my %animaux_noms_et_types = (
Lucky   => 'chien',
Rodney  => 'chien',
Tuxedo  => 'chat',
Petunia => 'chat',
Rosie   => 'chien',
);

afficher_animaux( %animaux_noms_et_types );

sub afficher_animaux
{
my %animaux = @_;
while (my ($nom, $type) = each %animaux)
{
say "$nom est un $type";
}
}

Lorsque Perl aplatit %animaux_noms_et_types dans une liste, l'ordre des paires clé/valeur du hachage varie, mais la liste contiendra toujours une clé suivie immédiatement par sa valeur. L'affectation du hachage à l'intérieur de afficher_animaux() fonctionne de la même manière que l'affectation explicite à %animaux_noms_et_types.

Cet aplatissement est souvent utile, mais évitez de mélanger des scalaires et des variables composites aplaties en listes de paramètres. Pour écrire une fonction afficher_animaux_par_type() où l'un des paramètres est le type d'animal de compagnie à afficher, passez le type comme premier paramètre (ou utilisez pop pour le supprimer de la fin de @_, si vous aimez dérouter les gens) :

sub afficher_animaux_par_type
{
my ($type, %animaux) = @_;

while (my ($nom, $espece) = each %animaux)
{
next unless $espece eq $type;
say "$nom est un $espece";
}
}

my %animaux_noms_et_types = (
Lucky   => 'chien',
Rodney  => 'chien',
Tuxedo  => 'chat',
Petunia => 'chat',
Rosie   => 'chien',
);

afficher_animaux_par_type( 'chien',   %animaux_noms_et_types );
afficher_animaux_par_type( 'chat',    %animaux_noms_et_types );
afficher_animaux_par_type( 'orignal', %animaux_noms_et_types );

VI-C-2 - Gourmandise

L'affectation de liste d'une variable agrégée est toujours gourmande, donc l'affectation à %animaux absorbe (slurps) toutes les valeurs restantes de @_. Si le paramètre $type se trouve à la fin de @_, Perl émettrait un avertissement sur l'affectation d'un nombre impair d'éléments au hachage. Vous pourriez contourner cela :

sub afficher_animaux_par_type
{
my $type    = pop;
my %animaux = @_;

...
}

... au détriment de la clarté. Le même principe s'applique lors de l'affectation d'un tableau comme paramètre. Utilisez des références (Références) pour éviter l'aplatissement global indésirable.

VI-C-3 - Gestion des alias

Le tableau @_ contient une subtilité : il crée des alias sur les arguments des fonctions. Autrement dit, vous pouvez modifier les arguments passés à la fonction si vous accédez directement à @_ :

sub modifier_nom
{
$_[0] = reverse $_[0];
}

my $nom = 'Orange';
modifier_nom( $nom );
say $nom;

# affiche egnarO

Modifiez directement un élément de @_ et vous allez modifier l'argument initial. Soyez prudent et déballez le contenu de @_ rigoureusement ou documentez soigneusement la modification.

VI-D - Fonctions et espaces de noms

Chaque fonction possède un espace de noms conteneur (Paquetages). Les fonctions se trouvant dans un espace de noms non déclaré  fonctions non déclarées dans la portée d'un package déclaré explicitement  existent dans l'espace de noms main. Vous pouvez également déclarer une fonction au sein d'un autre espace de noms en la faisant précéder par le nom de celui-ci :

sub Extensions::Math::add { ... }

Cela déclarera la fonction et créera l'espace de noms si nécessaire. Rappelez-vous que les paquetages Perl sont ouverts à la modification à tout moment, même pendant l'exécution de votre programme. Si vous déclarez plusieurs fonctions du même nom dans le même espace de noms, Perl émettra un avertissement.

Vous pouvez vous référer à d'autres fonctions au sein d'un espace de noms par leurs noms courts. Utilisez un nom complet pour appeler une fonction d'un autre espace de noms :

package main;

Extensions::Math::add( $scalar, $vector );

N'oubliez pas, les fonctions sont visibles à l'extérieur de leur propre espace de noms par leur nom complet. Vous pouvez également importer des noms à partir d'autres espaces de noms.

Fonctions lexicales

Perl 5.18 a ajouté une fonctionnalité expérimentale de déclaration lexicale des fonctions. Après déclaration, elles sont visibles seulement à l'intérieur de la portée lexicale dans laquelle elles sont déclarées. Voir la section « Lexical Subroutines » de perldoc perlsub pour plus de détails.

VI-D-1 - Importation

Lors du chargement d'un module avec la commande interne use (Modules), Perl appelle automatiquement une méthode nommée import(). Les modules peuvent fournir leur propre méthode import(), ce qui rend disponibles dans le paquetage appelant tous les symboles définis ou une partie de ceux-ci. Les arguments qui suivent le nom du module dans l'instruction use sont transmis à la méthode import() du module. Ainsi :

use strict;

... charge le module strict.pm et appelle strict->import() sans arguments, tandis que :

use strict 'refs';
use strict qw( subs vars );

... charge le module strict.pm, appelle strict->import( 'refs' ), puis appelle strict->import('subs', vars ').

use a un comportement particulier en ce qui concerne import(), mais vous pouvez appeler import() directement. L'exemple use ci-dessus est équivalent à :

BEGIN
{
require strict;
strict->import( 'refs' );
strict->import( qw( subs vars ) );
}

La commande interne use ajoute un bloc implicite BEGIN autour de ces déclarations afin que l'appel de import() ait lieu immédiatement après que l'analyseur a compilé toute l'instruction use. Cela garantit que l'analyseur connaît tous les symboles importés par strict avant de compiler le reste du programme. Dans le cas contraire, toute fonction importée d'un autre module, mais non déclarée dans le fichier courant ressemblerait à un « mot nu » et enfreindrait strict, par exemple.

Bien sûr, strict est un pragma (Pragmas), alors il a d'autres effets.

VI-E - Rapports d'erreurs

Presque chaque fonction a un appelant. Utilisez la commande interne caller pour inspecter le contexte d'appel d'une fonction. Utilisée sans aucun argument, caller retourne une liste contenant le nom du paquetage appelant, le nom du fichier contenant l'appel et le numéro de la ligne du fichier sur laquelle l'appel a eu lieu :

package main;

main();

sub main
{
montrer_information_appelant();
}

sub montrer_information_appelant
{
my ($package, $file, $ligne) = caller();
say "Appelée à partir de $package à $file:$ligne";
}

La chaîne complète des appels est disponible pour inspection. Passez un argument entier unique n à caller() pour remonter à l'appelant de l'appelant de l'appelant de rang n. Autrement dit, si montrer_information_appelant() utilise caller(0), elle recevrait des informations sur l'appel depuis la méthode main(). Si elle utilise caller(1), elle recevrait des informations sur l'appel du début du programme.

Cet argument facultatif indique également à caller de fournir des valeurs de retour supplémentaires, y compris le nom de la fonction et le contexte de l'appel :

sub montrer_information_appelant
{
my ($package, $file, $ligne, $fonc) = caller(0);
say "$fonc appelée à partir de $package à $file:$ligne";
}

Le module standard Carp utilise caller pour signaler des erreurs et lancer des avertissements dans les fonctions. Lorsqu'il est utilisé à la place de die dans le code de la bibliothèque, croak() lève une exception du point de vue de l'appelant. carp() signale un avertissement à partir du fichier et le numéro de la ligne de l'appelant (Produire des avertissements).

Utilisez caller (ou Carp) lors de la validation des paramètres ou des conditions préalables d'une fonction pour indiquer que ce qui a appelé la fonction l'a mal fait.

VI-E-1 - Valider les arguments

Tout en faisant de son mieux pour faire ce que vous lui demandez, Perl offre quelques façons natives de tester la validité des arguments fournis à une fonction. Évaluez @_ en contexte scalaire pour vérifier si le nombre de paramètres passés à une fonction est correct :

sub ajouter_nombres
{
croak 'Attendait deux nombres, a reçu : ' . @_
unless @_ == 2;

...
}

Cette validation signale toute erreur concernant le nombre de paramètres du point de vue de l'appelant, grâce à l'utilisation de croak.

La vérification du type est plus difficile, en raison des conversions de type effectuées par les opérateurs de Perl (Contexte). Si vous voulez une sécurité supplémentaire des paramètres de la fonction, regardez les modules CPAN tels que Params::Validate ou MooseX::Method::Signatures.

VI-F - Fonctions avancées

Les fonctions sont la base de nombreuses fonctionnalités Perl avancées.

VI-F-1 - Sensibilisation au contexte

Les fonctions internes de Perl savent si vous les avez invoquées dans un contexte vide, scalaire ou de liste. Vos fonctions peuvent aussi le savoir. La fonction interne malnommée wantarray (voir perldoc -f wantarray) retourne undef pour signaler le contexte vide, une valeur fausse pour signaler un contexte scalaire et une valeur vraie pour signaler un contexte de liste.

sub sensible_au_contexte
{
my $contexte = wantarray();

return qw( Contexte de liste ) if         $contexte;
say    'Contexte vide'     unless defined $contexte;
return 'Contexte scalaire' unless         $contexte;
}

sensible_au_contexte();
say my $scalaire = sensible_au_contexte();
say sensible_au_contexte();

Cela peut être utile pour éviter que des fonctions qui pourraient produire des valeurs de retour coûteuses le fassent dans un contexte vide. Certaines fonctions idiomatiques retournent une liste dans un contexte de liste et le premier élément de la liste ou une référence de tableau dans un contexte scalaire. Rappelez-vous, cependant, qu'il n'existe pas d'unique meilleure recommandation pour l'utilisation de wantarray. Parfois, c'est plus clair d'écrire des fonctions distinctes et sans ambiguïté, comme trouver_garnitures_toutes() et trouver_garniture_suivante().

Mise en contexte

Les modules CPAN Want de Robin Houston et Contextual::Return de Damian Conway offrent de nombreuses possibilités pour écrire des interfaces puissantes sensibles au contexte.

VI-F-2 - Récursivité

Supposons que vous voulez trouver un élément dans un tableau trié. Vous pouvez itérer sur chaque élément du tableau individuellement à la recherche de la cible, mais vous aurez à examiner en moyenne la moitié des éléments du tableau. Une autre approche consiste à diviser le tableau en deux, choisir l'élément du milieu, comparer, puis répéter l'opération avec la moitié inférieure ou supérieure selon le résultat de la comparaison. Diviser pour régner. Lorsque vous épuisez les éléments à inspecter ou vous trouvez l'élément, arrêter.

Un test automatisé pour cette technique pourrait être :

use Test::More;

my @elements =
(
1, 5, 6, 19, 48, 77, 997, 1025, 7777, 8192, 9999
);

ok   elem_existe(     1, @elements ),
'trouvé le premier élément du tableau';
ok   elem_existe(  9999, @elements ),
'trouvé le dernier élément du tableau';
ok ! elem_existe(   998, @elements ),
'pas trouvé, élément absent du tableau';
ok ! elem_existe(    -1, @elements ),
'pas trouvé, élément absent du tableau';
ok ! elem_existe( 10000, @elements ),
'pas trouvé, élément absent du tableau';

ok   elem_existe(    77, @elements ),
'trouvé élément du milieu du tableau';
ok   elem_existe(    48, @elements ),
'trouvé la fin de la moitié inférieure du tableau';
ok   elem_existe(   997, @elements ),
'trouvé le début de la moitié supérieure du tableau';

done_testing();

La récursivité est un concept d'une simplicité trompeuse. Chaque appel à une fonction en Perl crée un nouveau contexte d'appel (call frame), une structure de données interne à Perl qui représente le fait d'avoir appelé une fonction. Il contient l'environnement lexical de l'invocation actuelle  les valeurs de toutes les variables lexicales au sein de la fonction invoquée. Le stockage des valeurs des variables lexicales étant séparé de la fonction elle-même, vous pouvez avoir plusieurs appels actifs à une fonction en même temps. Une fonction peut même s'appeler elle-même, récursivement.

Pour passer le test précédent, écrivons la fonction récursive elem_existe() ainsi :

sub elem_existe
{
my ($item, @array) = @_;

# arrêter la recherche récursive si pas d'élément à rechercher
return unless @array;

# descendre d'un demi-cran en cas d'un nombre pair d'éléments
my $milieu = int( (@array / 2) - 0.5 );
my $element_milieu = $array[ $milieu ];

# retourner vrai si l'élément a été trouvé
return 1 if $item  == $element_milieu;

# retourner faux si un seul élément dans le tableau
return   if @array == 1;

# recherche récursive dans la moitié inférieure du tableau
return elem_existe(
$item, @array[0 .. $milieu]
) if $item < $element_milieu;

# recherche récursive dans la moitié supérieure du tableau
return elem_existe(
$item, @array[ $milieu + 1 .. $#array ]
);
}

Gardez à l'esprit que les arguments de la fonction seront différents pour chaque appel, sinon la fonction se comporterait toujours de la même façon (elle continuera la récursion jusqu'à ce que le programme plante). C'est pour cela que la condition d'arrêt est si importante.

Tout programme récursif peut être écrit sans récursivité (voir le livre Higher Order Perl à http://hop.perl.plover.com/), mais cette approche consistant à diviser pour régner est un moyen efficace de gérer de nombreux types de problèmes similaires.

VI-F-3 - Variables lexicales

Comme impliqué par la récursivité, chaque appel d'une fonction crée sa propre instance de portée lexicale représentée en interne par un contexte d'appel. Même si la déclaration de elem_existe() crée une seule portée pour les lexicales $item, @array, $midpoint et $miditem, chaque appel à elem_existe()  même récursif  stocke les valeurs de ces variables lexicales séparément.

Non seulement elem_existe() peut sappeler elle-même, mais les variables lexicales de chaque appel sont protégées et séparées :

use Carp 'cluck';

sub elem_existe
{
my ($item, @array) = @_;

cluck "[$item] (@array)";
...
}

VI-F-4 - Appels de fonctions et récursion terminale

Un inconvénient de la récursivité est que vous devez gérer correctement les valeurs de retour, faute de quoi votre fonction risque de sappeler indéfiniment. Pour cette raison, la fonction elem_existe() a plusieurs instructions return. Perl offre un avertissement utile Deep recursion on subroutine lorsqu'il soupçonne une récursivité incontrôlée. La limite fixée à 100 appels récursifs est arbitraire, mais souvent utile. Vous pouvez désactiver cet avertissement avec no warnings 'recursion'.

Comme chaque appel à une fonction nécessite un nouveau contexte d'appel et de l'espace de stockage lexical, le code hautement récursif peut utiliser plus de mémoire que le code itératif. L'élimination des récursions terminales peut aider.

Une récursion terminale est un appel à une fonction qui retourne directement les résultats de cette fonction. Ces appels récursifs à elem_existe() :

# recherche récursive dans la moitié inférieure du tableau
return elem_existe(
$item, @array[0 .. $milieu]
) if $item < $element_milieu;

# recherche récursive dans la moitié supérieure du tableau
return elem_existe(
$item, @array[ $milieu + 1 .. $#array ]
);

... sont candidats à l'élimination de la récursion terminale. Cette optimisation éviterait de retourner à l'appel courant, puis à l'appel parent et ainsi de suite. Au lieu de cela, elle retourne directement à l'appel parent d'origine.

Perl n'élimine pas automatiquement les récursions terminales, mais vous pouvez obtenir le même effet en utilisant une forme particulière de la fonction goto. Contrairement à la forme qui produit souvent du code nommé spaghetti (parce que le flot d'exécution est aussi simple et direct qu'un plat de spaghettis), cette forme de la fonction goto remplace l'appel de fonction en cours par l'appel à une autre fonction. Vous pouvez utiliser une fonction par nom ou par référence. Vous pouvez même modifier les arguments passés à la fonction de remplacement en modifiant @_ :

# recherche récursive dans la moitié inférieure du tableau
if ($item < $element_milieu)
{
@_ = ($item, @array[0 .. $milieu]);
goto &elem_existe;
}

# recherche récursive dans la moitié supérieure du tableau
else
{
@_ = ($item, @array[$milieu + 1 .. $#array] );
goto &elem_existe;
}

Les optimisations sont parfois laides, mais si l'autre option est un code hautement récursif qui épuise la mémoire, choisissez la laideur et profitez de son côté pratique.

VI-G - Pièges et limitations

Perl prend toujours en charge l'ancien style d'appel de fonction des versions anciennes de Perl. Les versions précédentes de Perl nécessitaient une esperluette (&) devant l'appel de fonction. Perl 1 vous demandait même d'utiliser la commande interne do :

# style obsolète (Perl 4); évitez
my $resultat = &calculer_resultat( 52 );

# style Perl 1; évitez
my $resultat = do calculer_resultat( 42 );

# méli-mélo fou; à éviter à tout prix
my $resultat = do &calculer_resultat( 42 );

Non seulement cette syntaxe obsolète est visuellement encombrante, mais l'esperluette en début entraîne aussi des comportements surprenants. Tout d'abord, elle désactive toute vérification de prototype. Deuxièmement, elle passe implicitement le contenu de @_ non modifié, sauf si vous avez vous-même passé explicitement les arguments. Oui, c'est une action invisible à distance.

Un dernier piège vient de l'absence des parenthèses de l'appel de fonction. L'analyseur Perl utilise plusieurs heuristiques pour résoudre les « mots nus » ambigus et le nombre de paramètres passés à une fonction. Les heuristiques peuvent être erronées :

# avertissement; contient un bogue subtil
ok elem_existe 1, @elements, 'trouvé le premier élément';

L'appel à elem_existe() va engloutir la description du test conçu comme deuxième argument de ok(). Parce que elem_existe() utilise un deuxième paramètre gourmand (un tableau), cela peut passer inaperçu jusqu'au moment où Perl produit des avertissements sur la comparaison d'un non-nombre (la description du test, qu'il ne peut pas convertir en nombre) avec l'élément du tableau.

Il est vrai que des parenthèses superflues peuvent nuire à la lisibilité, mais une utilisation réfléchie des parenthèses peut clarifier le code et rendre improbables les bogues subtils.

VI-H - Portée

En Perl, la portée se réfère à la durée de vie et la visibilité des entités nommées. Tout ce qui porte un nom en Perl (une variable, une fonction, un descripteur de fichier, une classe) a une portée. La portée aide à appliquer l'encapsulation  maintenir ensemble des concepts connexes et empêcher la fuite de leurs détails.

VI-H-1 - Portée lexicale

La portée lexicale est la portée visible pour vous quand vous lisez un programme. Un bloc délimité par des accolades  que ce soit un bloc nu, le bloc d'une boucle ou d'une déclaration sub, un bloc eval, un bloc package ou tout bloc autre que ceux associés aux opérateurs de citation  crée une nouvelle portée. Le compilateur Perl résout cette portée pendant la compilation.

La portée lexicale décrit la visibilité des variables déclarées avec my  les variables lexicales. Une variable lexicale déclarée à l'intérieur d'une portée est visible dans cette portée et toutes les portées imbriquées intérieures, mais est invisible dans les portées sœurs ou dans les portées extérieures :

# portée lexicale extérieure
{
package Robot::Butler

# portée lexicale intérieure
my $niveau_batterie;

sub chambre_propre
{
# une autre portée lexicale plus intérieure
my $minuteur;

do {
# la portée lexicale la plus intérieure
my $ramassette;
...
} while (@_);

# la sœur de l'autre portée lexicale plus intérieure
for (@_)
{
# une portée lexicale plus intérieure séparée
my $chiffon;
...
}
}
}

... $niveau_batterie est visible dans les quatre portées. $minuteur est visible dans la méthode, le bloc do et la boucle for. $ramassette n'est visible que dans le bloc do et $chiffon uniquement dans la boucle for.

La déclaration dans une portée interne d'une variable lexicale du même nom que celui d'une variable lexicale déclarée dans une portée externe cache, ou occulte, la variable lexicale externe dans la portée interne.

Par exemple :

my $nom = 'Jacob';

{
my $nom = 'Edward';
say $nom;
}

say $nom;

Dans du code plus long avec des portées plus étendues, ce comportement d'occultation est souvent souhaitable  il est plus facile de comprendre le code lorsque la portée d'une variable lexicale n'est pas plus étendue que quelques dizaines de lignes.

Collisions de nom

L'occultation lexicale peut se produire accidentellement. Limitez la portée des variables et l'imbrication des portées afin de diminuer les risques.

Le petit exemple de programme d'occultation lexicale affiche Edward, puis Jacob parce que la variable lexicale dans la portée interne cache la variable lexicale dans la portée externe. L'occultation d'une variable lexicale est une caractéristique de l'encapsulation. La déclaration de plusieurs variables ayant le même nom et le même type dans la même portée lexicale produit un message d'avertissement.

Certaines déclarations lexicales ont des subtilités, par exemple une variable lexicale utilisée comme variable d'itération d'une boucle for. Sa déclaration se trouve en dehors du bloc, mais sa portée est à l'intérieur du bloc de la boucle :

my $chat = 'Brad';

for my $chat (qw( Jack Daisy Petunia Tuxedo Choco ))
{
say "Le chat de l'itération est $chat";
}

say "Le chat statique est $chat";

Les fonctions  nommés et anonymes  fournissent une portée lexicale à leur corps. Cela permet les fermetures (Fermetures).

VI-H-2 - La portée our

À l'intérieur d'une portée donnée, déclarez un alias pointant vers une variable de paquetage avec la fonction our. Comme my, our applique la portée lexicale de l'alias. Le nom complet est disponible partout, mais l'alias lexical n'est visible qu'à l'intérieur de sa portée.

our est surtout utile avec des variables globales de paquetage comme $VERSION et $AUTOLOAD. Vous obtenez un peu de détection typographique (la déclaration d'un paquetage global avec our satisfait la règle vars du pragma strict), mais vous avez toujours affaire à une variable globale.

VI-H-3 - Portée dynamique

La portée dynamique ressemble à la portée lexicale en matière de règles de visibilité, mais au lieu de regarder vers l'extérieur dans les portées de compilation, la recherche va en arrière à travers tous les appels de fonction que vous avez faits pour atteindre le code actuel. La portée dynamique s'applique uniquement aux variables globales et variables globales de paquetage (parce que les variables lexicales ne sont pas visibles en dehors de leur portée) et est plus facile à comprendre avec un exemple. Alors qu'une variable globale de paquetage peut être visible à l'intérieur de toutes les portées, sa valeur peut changer en fonction de la localisation et de l'affectation :

our $portee;

sub interne
{
say $portee;
}

sub main
{
say $portee;
local $portee = 'portée main()';
milieu();
}

sub milieu
{
say $portee;
interne();
}

$portee = 'portée externe';
main();
say $portee;

Le programme commence par déclarer une variable our, $portee, ainsi que trois fonctions. Il se termine par l'initialisation de $portee et l'appel à main().

Dans main(), le programme affiche la valeur actuelle de $portee, portée externe, puis localise la variable. Cela modifie la visibilité du symbole dans la portée lexicale courante ainsi que dans toutes les fonctions appelées à partir de la portée lexicale courante ; c'est cet « ainsi que »qui distingue la portée dynamique de la portée statique. Donc, $portee contient portée main() à la fois dans le corps de milieu() et interne(). Après le retour de main(), lorsque le flot d'exécution atteint la fin de son bloc, Perl restaure la valeur d'origine de $portee qui avait été provisoirement localisée. La dernière instruction say affiche une fois de plus portée externe.

Perl utilise des règles de visibilité et mécanismes de stockage différents pour les variables de paquetage et les variables lexicales. Chaque portée qui contient des variables lexicales utilise une structure de données appelée carnet lexical ou lexpad pour stocker les valeurs des variables lexicales incluses. Chaque fois que le flot d'exécution entre dans l'une de ces portées, Perl crée un autre carnet lexical pour contenir les valeurs des variables lexicales pour cet appel en particulier. Cela fait que les fonctions travaillent correctement, en particulier lors des appels récursifs (Récursivité).

Chaque paquetage a une table de symboles unique qui contient les variables de paquetage ainsi que les fonctions nommées. L'importation (Importation) fonctionne par l'inspection et la manipulation de cette table de symboles. Il en va de même pour local. Vous pouvez localiser uniquement des variables globales et variables globales de paquetage, jamais des variables lexicales.

local est le plus souvent utile avec des variables magiques. Par exemple, $/, le séparateur d'enregistrements en entrée, définit la quantité de données qu'une opération readline lira à partir d'un descripteur de fichier. $!, la variable d'erreur système, contient des détails d'erreur pour le dernier appel système. $@, la variable Perl d'erreur de eval, contient les erreurs de l'opération eval la plus récente. $|, la variable d'écriture immédiate (autoflush) des tampons attachés au descripteur de fichier, détermine si Perl écrit immédiatement dans le descripteur de fichier sélectionné après chaque opération d'écriture.

La localisation de ceux-ci dans la portée la plus étroite possible limite l'effet de vos modifications. Cela peut éviter des comportements étranges ailleurs dans votre code.

VI-H-4 - Portée statique

Perl 5.10 a ajouté une nouvelle portée pour prendre en charge la commande interne state. La portée d'état ressemble à la portée lexicale en matière de visibilité, mais ajoute une initialisation ainsi que la persistance de la valeur :

sub compteur
{
state $total = 1;
return $total++;
}

say compteur();
say compteur();
say compteur();

Lors du premier appel à compteur, Perl initialise $total. Lors des prochains appels, $total conserve sa valeur précédente. Ce programme imprime 1, 2 et 3. Remplacez state par my et le programme affichera 1, 1 et 1.

Vous pouvez utiliser une expression pour définir la valeur initiale d'une variable state :

sub  compteur
{
state $total = shift;
return $total++;
}

say compteur(2);
say compteur(4);
say compteur(6);

Même si une simple lecture du code peut suggérer que la sortie devrait être 2, 4 et 6, elle est en fait 2, 3 et 4. Le premier appel de compteur initialise la variable $total. Les appels suivants ne changeront pas sa valeur.

state peut être utile pour définir une valeur par défaut ou préparer un cache, mais assurez-vous de comprendre son comportement d'initialisation si vous l'utilisez :

sub compteur
{
state $total = shift;
say 'Second arg est : ', shift;
return $total++;
}

say counter(2, 'deux'  );
say counter(4, 'quatre');
say counter(6, 'six'   );

Le compteur de ce programme imprime 2, 3, et 4 comme prévu, mais les valeurs des seconds arguments destinés à compteur() sont deux, 4 et 6  parce que le shift du premier argument ne se produit que dans le premier appel de compteur(). Soit changez l'interface pour éviter cette erreur, soit écrivez ceci :

sub compteur
{
my ($valeur_initiale, $texte) = @_;

state $total = $valeur_initiale;
say "Second arg est : $texte";
return $total++;
}

say compteur(2, 'deux'  );
say compteur(4, 'quatre');
say compteur(6, 'six'   );

VI-I - Fonctions anonymes

Une fonction anonyme est une fonction sans nom. Elle se comporte exactement comme une fonction nommée : vous pouvez l'invoquer, lui passer des arguments, recevoir ses valeurs de retour et copier des références vers elle. Pourtant, la seule façon de l'utiliser c'est par référence (Références de fonctions), pas par nom.

Un idiome commun de Perl connu sous le nom de table de distribution (dispatch table) utilise des hachages pour associer des données en entrée à des comportements :

my %calculer =
(
plus     => \&additionner_deux_nombres,
moins    => \&soustraire_deux_nombres,
fois     => \&multiplier_deux_nombres,
);

sub additionner_deux_nombres { $_[0] + $_[1] }
sub soustraire_deux_nombres  { $_[0] - $_[1] }
sub multiplier_deux_nombres  { $_[0] * $_[1] }

sub calculer
{
my ($gauche, $op, $droit) = @_;

return unless exists $calculer{ $op };

return $calculer{ $op }->( $gauche, $droit );
}

La fonction calculer() prend des arguments de la forme (2, 'fois', 2) et renvoie le résultat de l'évaluation de l'opération. Si vous écrivez une application simple de calculatrice, vous pouvez utiliser calculer pour déterminer le calcul à accomplir sur base d'un nom fourni par un utilisateur.

VI-I-1 - Déclarer des fonctions anonymes

L'utilisation de l'instruction sub sans aucun nom crée et retourne une fonction anonyme. Utilisez cette référence de fonction à n'importe quel endroit où vous utiliseriez une référence vers une fonction nommée, comme pour déclarer les fonctions directement dans une table de distribution :

my %calculer =
(
plus      => sub { $_[0]  + $_[1] },
moins     => sub { $_[0]  - $_[1] },
fois      => sub { $_[0]  * $_[1] },
diviser   => sub { $_[0]  / $_[1] },
puissance => sub { $_[0] ** $_[1] },
);

Distribution défensive

Seules les fonctions présentes dans cette table de distribution peuvent être appelées par les utilisateurs. Si votre fonction calculer utilise une chaîne fournie par l'utilisateur comme nom littéral d'une fonction, un utilisateur malveillant pourrait appeler n'importe quelle fonction n'importe où en passant 'Internal::Functions::malicious_function'.

Vous pouvez voir également des fonctions anonymes passées comme arguments de fonction :

sub invoquer_fonction_anonyme
{
my $func = shift;
return $func->( @_ );
}

sub fonction_nommee
{
say 'Je suis une fonction nommée !';
}

invoquer_fonction_anonyme( \&fonction_nommee );
invoquer_fonction_anonyme( sub { say 'qui suis-je ?' } );

VI-I-2 - Noms de fonctions anonymes

Utilisez l'introspection (voir aussi la fonction sub_name du module CPAN Sub::Identify) pour déterminer si une fonction est nommée ou anonyme :

package ShowCaller;

sub afficher_appelant
{
my ($paquetage, $fichier, $ligne, $fonction) = caller(1);
say "Appelée par $fonction à $paquetage:$fichier:$ligne";
}

sub main
{
my $sub_anonyme = sub { afficher_appelant() };
afficher_appelant();
$sub_anonyme->();
}

main();

Le résultat peut être surprenant :

Appelée par ShowCaller::main

à ShowCaller:anoncaller.pl:20

Appelée par ShowCaller::__ANON__

à ShowCaller:anoncaller.pl:17

La __ANON__ sur la deuxième ligne de la sortie montre que la fonction anonyme n'a aucun nom que Perl puisse identifier. Cela peut compliquer le débogage. La fonction subname() du module CPAN Sub::Name vous permet d'attacher des noms aux fonctions anonymes :

use Sub::Name;
use Sub::Identify 'sub_nom';

my $anonyme  = sub {};
say sub_nom( $anonyme );

my $nommee = subname( 'pseudo-anonyme', $anonyme );
say sub_nom( $nommee  );
say sub_nom( $anonyme );

say sub_nom( sub {}   );

Ce programme affiche en sortie :

__ANON__

pseudo-anonyme

pseudo-anonyme

__ANON__

Sachez que les deux références désignent la même fonction anonyme sous-jacente. L'utilisation de subname() sur une référence de fonction modifiera le nom de cette fonction anonyme, de sorte que toutes les autres références vers elle verront le nouveau nom.

VI-I-3 - Fonctions anonymes implicites

Perl vous permet de déclarer des fonctions anonymes comme arguments d'une fonction sans utiliser le mot-clé sub. Bien que cette fonctionnalité existe nominalement pour permettre aux programmeurs d'écrire leur propre syntaxe comme celle pour map et eval (Prototypes), un exemple intéressant est l'utilisation des fonctions qui ne ressemblent pas à des fonctions. Ce n'est pas parfait, mais peut rendre le code plus facile à lire.

Examinons le module Test::Fatal du CPAN, qui prend une fonction anonyme comme premier argument de sa fonction exception() :

use Test::More;
use Test::Fatal;

my $croaker = exception { die 'I croak!' };
my $liver   = exception { 1 + 1 };

like( $croaker, qr/I croak/, 'die() should croak'   );
is(   $liver,   undef,       'addition should live' );

done_testing();

Vous pourriez réécrire cela de façon plus explicite :

my $croaker = exception( sub { die 'I croak!' } );
my $liver   = exception( sub { 1 + 1 } );

... ou passer par référence des fonctions nommées :

sub croaker { die 'I croak!' }
sub liver   { 1 + 1 }

my $croaker = exception \&croaker;
my $liver   = exception \&liver;

like( $croaker, qr/I croak/, 'die() should die'     );
is(   $liver,   undef,       'addition should live' );

... mais vous ne pouvez pas les passer comme des références scalaires :

my $croak_ref = \&croaker;
my $live_ref  = \&liver;

# BOGUE: ne fonctionne pas
my $croaker   = exception $croak_ref;
my $liver     = exception $live_ref;

... parce que le prototype change la façon dont l'analyseur Perl interprète ce code. Il ne peut pas déterminer avec 100 % de clarté que contiendront $croaker et $liver et lèvera une exception.

Type of arg 1 to Test::Fatal::exception must be block or sub {} (not private variable)

Sachez également qu'une fonction qui accepte une fonction anonyme comme le premier de plusieurs arguments ne peut pas avoir une virgule de fin après le bloc de fonction :

use Test::More;
use Test::Fatal 'dies_ok';

dies_ok { die 'This is my boomstick!' }
'No movie references here';

C'est un inconvénient parfois déroutant de la syntaxe autrement utile, à cause d'une bizarrerie de l'analyseur Perl. La clarté syntaxique disponible en favorisant des blocs nus dans les fonctions anonymes peut être utile, mais utilisez-la avec parcimonie et documentez l'API avec soin.

VI-J - Fermetures

Chaque fois que le flot d'exécution entre dans une fonction, cette fonction reçoit un nouvel environnement représentant la portée lexicale de cet appel (Portée ). Cela s'applique aussi bien aux fonctions anonymes (Fonctions anonymes). L'implication est puissante. Le terme informatique fonctions d'ordre supérieur se réfère aux fonctions qui manipulent d'autres fonctions. Les fermetures mettent en valeur ce pouvoir.

VI-J-1 - Création de fermetures

Une fermeture est une fonction qui utilise les variables lexicales d'une portée externe. Vous avez probablement déjà créé et utilisé des fermetures sans vous en rendre compte :

use Modern::Perl '2014';

my $nom_fichier = shift @ARGV;

sub get_nom_fichier { return $nom_fichier }

Si ce code vous semble simple, tant mieux ! Bien sûr, la fonction get_nom_fichier() peut voir la variable lexicale $nom_fichier. C'est ainsi que la portée fonctionne !

Supposons que vous vouliez parcourir une liste d'éléments sans avoir à gérer vous-même la variable d'itération. Vous pouvez créer une fonction qui retourne une fonction qui, une fois invoquée, retournera l'élément suivant dans l'itération :

sub creer_iterateur
{
my @elements = @_;
my $compteur = 0;

return sub
{
return if $compteur == @elements;
return $elements[ $compteur++ ];
}
}

my $cousins = creer_iterateur(qw(
Rick Alex Kay Eric Corey Mandy Christine Alex
));

say $cousins->() for 1 .. 6;

Même si le retour de creer_iterateur() a eu lieu, la fonction anonyme stockée dans $cousins « s'est fermée » sur les valeurs de ces variables telles qu'elles existaient au sein de l'invocation de creer_iterateur(). Leurs valeurs persistent (Comptage des références).

Parce que l'appel de creer_iterateur() crée un environnement lexical distinct, la fonction anonyme qu'il crée et retourne se referme un environnement lexical unique pour chaque appel :

my $tantes = creer_iterateur(qw(
Carole Phyllis Wendy Sylvia Monica Lupe
));

say $cousins->();
say $tantes->();

Comme creer_iterateur() ne retourne pas ces variables lexicales par valeur ou par référence, aucun code Perl autre que la fermeture ne peut y accéder. Elles sont encapsulées aussi efficacement que n'importe quelle autre lexicale, bien que n'importe quel code qui partage un environnement lexical puisse accéder à ces valeurs. Cet idiome offre une meilleure encapsulation de ce qui serait par ailleurs une variable globale à un fichier ou à un paquetage :

{
my $variable_privee;

sub set_privee { $variable_privee = shift }
sub get_privee { $variable_privee }
}

Sachez que vous ne pouvez pas imbriquer des fonctions nommées. Les fonctions nommées ont une portée globale de paquetage. Toutes les variables lexicales partagées entre les fonctions imbriquées ne le seront plus lorsque la fonction externe détruit son premier environnement lexical. Si cela vous semble confus, imaginez la mise en œuvre.

Intrusion dans la vie privée

Le module PadWalker du CPAN vous permet de violer l'encapsulation lexicale, mais toute personne qui l'utilise doit corriger les bogues qui en résultent.

VI-J-2 - Utilisations des fermetures

L'itération sur une liste de taille fixe avec une fermeture est intéressante, mais les fermetures peuvent faire beaucoup plus, comme itérer sur une liste qui est trop coûteuse à calculer ou trop grande pour la garder intégralement en mémoire. Considérons une fonction pour créer des suites de Fibonacci au fur et à mesure que vous avez besoin de ses éléments. Pour quoi faire ? Pour vérifier vos devoirs en langage de programmation Haskell. Au lieu de recalculer la série de manière récursive, utilisez un cache et créez paresseusement les éléments dont vous avez besoin :

sub gen_fib
{
my @fibs = (0, 1);
return sub
{
my $element = shift;
if ($element >= @fibs)
{
for my $calc (@fibs .. $element)
{
$fibs[$calc] = $fibs[$calc - 2]
+ $fibs[$calc - 1];
}
}
return $fibs[$element];
}
}

# calculer le quarante-deuxième nombre Fibonacci
my $fib = gen_fib();
say $fib->( 42 );

Chaque appel à la fonction retournée par gen_fib() prend un seul argument, le nième élément de la série de Fibonacci. La fonction génère et met en cache toutes les valeurs précédentes dans la série comme nécessaire, et renvoie l'élément demandé.

Voici où les fermetures et les fonctions de première classe deviennent intéressantes. Ce code fait deux choses ; il existe un modèle spécifique de mise en cache étroitement lié à la série numérique. Qu'advient-il si vous extrayez le code de cache spécifique (initialiser un cache, exécuter le code personnalisé pour charger en cache les éléments et retourner la valeur calculée ou mise en cache) à une fonction gen_caching_closure() ?

sub gen_caching_closure
{
my ($calc_element, @cache) = @_;
return sub
{
my $element = shift;
$calc_element->($element, \@cache)
unless $element < @cache;
return $cache[$element];
};
}

sub gen_fib
{
my @fibs = (0, 1, 1);
return gen_caching_closure( sub
{
my ($element, $fibs) = @_;
for my $calc ((@$fibs - 1) .. $element)
{
$fibs->[$calc] = $fibs->[$calc - 2]
+ $fibs->[$calc - 1];
}
}, @fibs
);
}

Le programme se comporte comme précédemment, mais maintenant les références de fonction et les fermetures séparent le comportement d'initialisation de cache du calcul du prochain nombre de la suite de Fibonacci. La personnalisation du comportement du code  dans ce cas, gen_caching_closure()  en passant en argument une fonction permet une grande flexibilité et peut nettoyer votre code.

Réduire, appliquer et filtrer

Les commandes internes map, grep et sort sont elles-mêmes des fonctions d'ordre supérieur.

VI-J-3 - Fermetures et application partielle

Les fermetures peuvent également supprimer la généricité non désirée. Prenons le cas d'une fonction qui accepte plusieurs paramètres :

sub preparer_coupe_glace
{
my %args         = @_;

my $creme_glacee = get_creme_glacee( $args{creme_glacee} );
my $banane       = get_banane(       $args{banane}       );
my $sirop        = get_sirop(        $args{sirop}        );
...
}

Les multiples possibilités de personnalisation pourraient fonctionner très bien dans une glacerie, mais pour un triporteur pour vente ambulante de glaces où vous servez seulement de la glace française à la vanille sur banane Cavendish, chaque appel à preparer_coupe_glace() passe toujours les mêmes arguments.

Une application partielle vous permet de lier maintenant certains des arguments à une fonction afin que vous puissiez fournir les autres plus tard. Enveloppez dans une fermeture la fonction que vous souhaitez appeler et passez les arguments liés. Pour votre triporteur de glaces :

my $preparer_coupe_triporteur = sub
{
return preparer_coupe_glace( @_,
creme_glacee => 'Vanille française',
banane       => 'Cavendish',
);
};

Maintenant, chaque fois que vous traitez une commande, invoquez la référence de fonction $preparer_coupe_triporteur et passez uniquement les arguments intéressants.(6) Vous n'allez jamais oublier ou transmettre de manière incorrecte les invariants. Vous pouvez même utiliser Sub::Install de CPAN pour importer la fonction $preparer_coupe_triporteur dans un autre espace de noms.

Ce n'est que le début de ce que vous pouvez faire avec les fonctions d'ordre supérieur. Le livreHigher Order Perl de Mark Jason Dominus est la référence canonique sur les fonctions de première classe et les fermetures de Perl. Vous pouvez le lire gratuitement en ligne à http://hop.perl.plover.com/(7).

VI-K - Variables statiques ou fermetures

Les fermetures (Fermetures) utilisent la portée lexicale (Portée) pour contrôler l'accès aux variables lexicales  même par des fonctions nommées :

{
my $securite = 0;

sub activer_securite    { $securite = 1 }
sub desactiver_securite { $securite = 0 }

sub entreprendre_action_geniale
{
return if $securite;
...
}
}

Ces fonctions encapsulent toutes les trois cet état partagé sans exposer directement la variable lexicale au code externe. Cet idiome fonctionne bien pour les cas où des fonctions multiples accèdent à cette variable lexicale, mais c'est un peu maladroit quand il n'y a qu'une seule fonction qui le fait. Supposons que chaque centième client de la glacerie reçoive un saupoudrage de granulés de chocolat gratuit :

my $compteur_client = 0;

sub servir_client
{
$compteur_client++;
my $commande = shift;

ajouter_granules($commande) if $compteur_client % 100 == 0;
...
}

Cette approche fonctionne, mais la création d'une nouvelle portée lexicale externe pour une seule fonction est un peu bruyante. Le déclarateur state vous permet de déclarer une variable de portée lexicale ayant une valeur qui persiste entre chaque appel :

sub servir_client
{
state $compteur_client = 0;
$compteur_client++;

my $commande = shift;
ajouter_granules($commande) if ($compteur_client % 100 == 0);

...
}

Vous devez activer explicitement cette fonctionnalité en utilisant un module tel que Modern::Perl, le pragma feature (Pragmas), ou en exigeant les caractéristiques d'une version Perl égale ou supérieure à la 5.10 (use 5.010;, use 5.012; et ainsi de suite).

state fonctionne également dans les fonctions anonymes :

sub comptage
{
return sub
{
state $compteur = 0;
return $compteur++;
}
}

... bien qu'il y ait peu d'avantages évidents de cette approche.

VI-L - Statique ou pseudostatique

Dans les anciennes versions de Perl, une fonction nommée pourrait se fermer sur sa portée lexicale précédente en abusant d'une bizarrerie de mise en œuvre. L'utilisation d'un modificateur d'instruction conditionnel postfixé ayant la valeur fausse avec une déclaration my évitait de réinitialiser une variable lexicale à undef ou à sa valeur d'initialisation.

Dans les versions modernes de Perl, toute utilisation d'un modificateur d'expression conditionnel postfixé qui modifie la déclaration d'une variable lexicale produit un avertissement d'obsolescence. Il est trop facile d'écrire par inadvertance du code bogué avec cette technique ; utilisez state là où c'est possible, ou une vraie fermeture sinon. Réécrivez cet idiome lorsque vous le rencontrez :

sub state_inattentif
{
# my $compteur  = 1 if 0; # OBSOLETE; ne pas utiliser
state $compteur = 1;      # préférable

...
}

Vous pouvez initialiser une variable statique uniquement avec une valeur scalaire. Si vous avez besoin de garder la trace d'une variable composite, utilisez une référence de hachage ou de tableau (Références).

VI-M - Attributs

En Perl, les entités nommées  variables et fonctions  peuvent avoir des métadonnées supplémentaires. Ces métadonnées prennent la forme d'attributs, noms et valeurs arbitraires utilisés avec certains types de métaprogrammation (Génération de code).

La syntaxe de déclaration d'attributs est étrange et l'utilisation efficace des attributs est plus un art qu'une science. La plupart des programmes ne les utilisent jamais, mais lorsqu'ils sont bien utilisés, ils offrent des avantages de clarté et de maintenance.

Un attribut simple est un identifiant précédé par un caractère deux-points, attaché à une déclaration :

my $forteresse      :cachee;

sub eruption_volcan :ProjetScientifique { ... }

Lorsque Perl analyse ces déclarations, il appelle les gestionnaires d'attributs nommés cachee et ProjetScientifique, s'ils existent pour les types appropriés (scalaires et fonctions, respectivement). Ces gestionnaires peuvent tout faire. Si les gestionnaires appropriés n'existent pas, Perl lancera une exception à la compilation.

Les attributs peuvent inclure une liste de paramètres. Perl traite ces paramètres comme listes de chaînes constantes. Le module Test::Class de CPAN utilise tels arguments paramétriques à bon escient (voir le framework Web Catalyst pour un autre exemple, différent) :

sub setup_tests          :Test(setup)    { ... }
sub test_monkey_creation :Test(10)       { ... }
sub shutdown_tests       :Test(teardown) { ... }

L'attribut Test identifie des méthodes qui incluent des assertions de test et éventuellement identifie le nombre d'assertions que la méthode a l'intention d'exécuter. Alors que l'introspection (Réflexion) de ces classes pourrait découvrir les méthodes de test appropriées, compte tenu des heuristiques solides bien conçues, l'attribut :Test est sans ambiguïté. Test::Class fournit des gestionnaires d'attributs qui gardent une trace de ces méthodes. Lorsque la classe a fini l'analyse, Test::Class peut effectuer une boucle sur la liste des méthodes de test et les exécuter.

Les paramètres setup et teardown permettent aux classes de test de définir leurs propres méthodes de mise en œuvre sans se soucier de conflits avec d'autres méthodes du même nom dans d'autres classes. Cela sépare l'idée de ce que cette classe doit faire de la façon dont les autres classes font leur travail. Sinon, une classe de test pourrait avoir une méthode nommée setup et une nommée teardown et devrait tout faire dans ces méthodes, ensuite appeler les méthodes parentes et ainsi de suite.

VI-M-1 - Inconvénients des attributs

Les attributs ont leurs inconvénients. Le pragma canonique pour travailler avec des attributs (le module attributes) répertorie son interface comme expérimentale depuis de nombreuses années, et à juste titre. Le module standard Attribute::Handlers de Damian Conway est beaucoup plus facile à utiliser et Attribute::Lexical de Andrew Main est une approche plus récente. Préférez l'un des deux à attributes lorsque c'est possible.

Le pire aspect des attributs, c'est qu'ils le rendent facile à déformer la syntaxe de Perl de façon imprévisible. Il n'est pas toujours facile de prédire ce que fera le code contenant des attributs. Une bonne documentation aide, mais si une déclaration apparemment innocente d'une variable lexicale stocke quelque part une référence vers cette variable, vos attentes sur sa durée de vie peuvent être erronées. De même, un gestionnaire peut envelopper une fonction dans une autre fonction et la remplacer dans la table de symboles à votre insu  imaginez un attribut :memoize qui appelle automatiquement le module standard Memoize.

Les attributs peuvent vous aider à résoudre des problèmes difficiles ou rendre une API plus facile à utiliser. Lorsqu'ils sont utilisés correctement ils sont puissants, mais la plupart des programmes n'en ont jamais besoin.

VI-N - AUTOLOAD

Perl ne vous oblige pas à déclarer chaque fonction avant de l'appeler. Perl tentera volontiers d'appeler une fonction, même si elle n'existe pas. Examinons le programme :

use Modern::Perl;

cuire_tarte( fruit => 'pomme' );

Lorsque vous l'exécutez, Perl lancera une exception en raison de l'appel de la fonction non définie cuire_tarte().

Maintenant, ajoutez une fonction appelée AUTOLOAD() (« autochargement ») :

sub AUTOLOAD {}

À présent, lorsque vous exécutez le programme il ne se passera rien d'évident. Perl appellera une fonction nommée AUTOLOAD() d'un paquetage  si elle existe  chaque fois que la recherche normale d'une méthode dans l'arbre d'héritage échoue. Modifiez AUTOLOAD() pour afficher un message qui démontre qu'elle est appelée :

sub AUTOLOAD { say 'Dans AUTOLOAD() !' }

La fonction AUTOLOAD() reçoit les arguments passés à la fonction non définie dans @_ et le nom complet de la fonction non définie (ici, main::cuire_tarte) dans la variable globale de paquetage $AUTOLOAD :

sub AUTOLOAD
{
our $AUTOLOAD;

# afficher les arguments
local $" = ', ';
say "Dans AUTOLOAD(@_) for $AUTOLOAD !"
}

Extrayez le nom de la méthode avec une expression régulière (Expressions régulières etcorrespondance) :

sub AUTOLOAD
{
my ($nom) = our $AUTOLOAD =~ /::(\w+)$/;

# afficher les arguments
local $" = ', ';
say "Dans AUTOLOAD(@_) for $nom !"
}

Jusqu'ici, ces exemples ont simplement intercepté les appels à des fonctions non définies. Vous avez d'autres options.

VI-N-1 - Renvoyer des méthodes dans AUTOLOAD()

Un modèle commun dans la programmation OO (Moose) est de déléguer ou d'envoyer par proxy certaines méthodes d'un objet à un autre objet en quelque sorte accessible au premier. Un proxy de journalisation peut faciliter le débogage :

package Proxy::Log;

# le constructeur lie une référence à un scalaire

sub AUTOLOAD
{
my ($nom) = our $AUTOLOAD =~ /::(\w+)$/;
Log::method_call( $nom, @_ );

my $self = shift;
return $$self->$nom( @_ );
}

Cette AUTOLOAD() extrait le nom de la méthode non définie. Ensuite, elle déréférence l'objet envoyé par proxy à partir d'une référence scalaire liée, enregistre l'appel de méthode, puis appelle cette méthode sur l'objet proxy avec les paramètres fournis.

VI-N-2 - Générer du code dans AUTOLOAD()

Cette double recherche est facile à écrire, mais inefficace. Chaque appel de méthode sur le proxy doit d'abord échouer dans sa recherche de méthode dans l'arbre d'héritage avant de se retrouver enfin dans AUTOLOAD(). Payez cette pénalité juste une seule fois par l'installation de nouvelles méthodes dans la classe proxy lorsque le programme a besoin d'elles :

sub AUTOLOAD
{
my ($nom)   = our $AUTOLOAD =~ /::(\w+)$/;
my $methode = sub { ... };

no strict 'refs';
*{ $AUTOLOAD } = $methode;
return $methode->( @_ );
}

Le corps de la fonction AUTOLOAD() précédente est devenu une fermeture (Fermetures) liée au nom de la méthode non définie. L'enregistrement de cette fermeture dans la table de symboles appropriée permet à toutes les recherches ultérieures de la méthode de trouver la fermeture créée (et d'éviter AUTOLOAD()). Finalement, ce code appelle directement la méthode et retourne le résultat.

Bien que cette approche soit plus propre et presque toujours plus transparente que la manipulation du comportement directement dans AUTOLOAD(), le code appelé par AUTOLOAD() peut voir AUTOLOAD() dans la liste de ses appelants en invoquant caller(). Si cela survient, non seulement ce serait une violation de l'encapsulation, la fuite des détails sur comment un objet fournit une méthode violerait également l'encapsulation.

Certains programmes utilisent des appels de fonction terminaux (Tailcalls) pour remplacer l'invocation courante de AUTOLOAD() par un appel à la méthode de destination :

sub AUTOLOAD
{
my ($nom)   = our $AUTOLOAD =~ /::(\w+)$/;
my $methode = sub { ... }

no strict 'refs';
*{ $AUTOLOAD } = $methode;
goto &$methode;
}

Cela a le même effet que l'appel direct de $methode, sauf que AUTOLOAD() n'apparaîtra plus dans la liste des appelants retournée par caller(), ce qui donne l'impression que la méthode générée a été appelée directement.

VI-N-3 - Inconvénients de AUTOLOAD

AUTOLOAD() peut être utile, mais il est difficile de l'utiliser correctement. L'approche naïve à générer des méthodes à l'exécution signifie que la méthode can() ne donnera pas des informations correctes sur les capacités des objets et des classes. La solution la plus simple est de déclarer préalablement toutes les fonctions que vous prévoyez utiliser dans AUTOLOAD() avec le pragma subs :

use subs qw( rouge vert bleu ocre cyan );

Vous les voyez maintenant

Les déclarations anticipées ne sont utiles que dans les deux cas rares d'utilisation des attributs et d'utilisation d'AUTOLOAD (AUTOLOAD).

Cette technique documente bien votre intention, mais vous oblige à maintenir une liste statique de fonctions ou méthodes. Surcharger la méthode can() (Le paquetage UNIVERSAL) fonctionne mieux parfois :

sub can
{
my ($self, $methode) = @_;

# utiliser les résultats  de la méthode can() parente
my $meth_ref = $self->SUPER::can( $methode  );
return $meth_ref if $meth_ref;

# ajouter un filtre ici
return unless $self->doit_generer( $methode );

$meth_ref = sub { ... };
no strict 'refs';
return *{ $methode } = $meth_ref;
}

sub AUTOLOAD
{
my ($self) = @_;
my ($nom)  = our $AUTOLOAD =~ /::(\w+)$/;>

return unless my $meth_ref = $self->can( $nom );
goto &$meth_ref;
}

AUTOLOAD() est un gros marteau manquant de délicatesse et peut attraper des fonctions et méthodes que vous n'aviez pas l'intention de charger automatiquement, comme DESTROY(), le destructeur d'objets. Si vous écrivez une méthode DESTROY() sans implémentation, Perl se fera un plaisir de pointer vers elle plutôt que vers AUTOLOAD() :

# éviter AUTOLOAD()
sub DESTROY {}

Une méthode très spéciale

Les méthodes spéciales import(), unimport() et VERSION() ne passent jamais par AUTOLOAD().

Si vous mélangez fonctions et méthodes dans un seul espace de noms qui hérite d'un autre paquetage qui fournit son propre AUTOLOAD(), vous risquez de voir cette erreur étrange :

Use of inherited AUTOLOAD for non-method slam_door() is deprecated

Si cela vous arrive, simplifiez votre code ; vous avez appelé une fonction qui n'existe pas dans un paquetage qui hérite d'une classe qui contient sa propre AUTOLOAD(). Le problème arrive de plusieurs manières : mélanger fonctions et méthodes dans un seul espace de noms est souvent un défaut de conception, l'héritage et AUTOLOAD() deviennent très rapidement complexes et raisonner sur le code est difficile lorsque vous ne savez pas quels objets les méthodes fournissent.

AUTOLOAD() est utile pour la programmation rapide et sale, mais un code robuste l'évite. (8)

VII - Expressions régulières et correspondance

Une grande partie de la puissance de traitement des données textuelles de Perl vient de son utilisation des expressions régulières. Une expression régulière (9) (également nommée regex ou regexp) est un motif qui décrit les caractéristiques d'un morceau de texte. Un moteur d'expressions régulières applique ces motifs pour reconnaître des parties de texte ou pour les remplacer.

La maîtrise des expressions régulières est une quête ardue, mais une connaissance même assez sommaire des regex vous donnera une grande puissance expressive. La documentation standard sur les regex de Perl comprend un tutoriel (perldoc perlretut), un guide de référence (perldoc perlreref), et une documentation complète (perldoc perlre). Le livre Mastering Regular Expressions de Jeffrey Friedl explique la théorie et la mécanique de la façon dont les expressions régulières fonctionnent.

VII-A - Variables littérales

Une expression régulière peut être une simple sous-chaîne :

my $lieu = 'Sambreville';
say 'Trouvé de l\'ambre !' if $lieu =~ /ambre/;

L'opérateur de correspondance ou de reconnaissance (m//, abrégé //) identifie dans cet exemple une expression régulière, ambre. Ce motif n'est pas un mot. Il signifie « le caractère a, suivi par le caractère m, suivi par le caractère b, suivi par un r, puis par un e ». Chaque caractère dans le motif est un élément indivisible (un atome). Un atome correspond ou pas.

L'opérateur de liaison des regex (=~) est un opérateur infixé (Fixité) qui applique l'expression régulière de son second opérande à une chaîne comme premier opérande. Dans un contexte scalaire, une correspondance est vraie si elle réussit. La forme négative de l'opérateur de liaison (!~) retourne vrai sauf si la correspondance réussit.

N'oubliez pas index !

La fonction index peut également rechercher une sous-chaîne littérale dans une chaîne (ainsi que rindex pour une recherche depuis la fin de la chaîne). L'utilisation d'un moteur de regex pour cela, c'est comme envoyer un drone de combat autonome au magasin du coin pour acheter du fromage  mais Perl vous permet d'écrire le code qui vous semble le plus clair.

L'opérateur de substitution, s///, est une sorte d'opérateur circumfixé (Fixité) avec deux opérandes. Lorsqu'il est utilisé avec l'opérateur de liaison des regex, son premier opérande (la partie entre le premier et le deuxième séparateur) est une expression régulière à comparer. Lorsqu'il est utilisé avec l'opérateur de liaison, le second opérande (la partie entre le deuxième et le troisième séparateur) est une sous-chaîne utilisée pour remplacer toutes les occurrences du premier opérande trouvées dans la chaîne initiale. Par exemple, pour guérir les allergies d'été gênantes :

my $etat = 'Je me sens mal.';
$etat    =~ s/mal/bien/;
say $etat;

VII-B - L'opérateur qr// et les combinaisons regex

L'opérateur qr// crée des expressions régulières de première classe. Utilisez une regex construite avec lui comme opérande de l'opérateur de correspondance ou comme premier opérande de l'opérateur de substitution :

my $nom = 'Sambreville';
my $ambre = qr/ambre/;
say 'Trouvé de l\'ambre !' if $nom =~ /$ambre/;

... ou combinez plusieurs objets regex en motifs complexes :

my $nom = 'Sambreville';
my $ambre = qr/ambre/;
my $ville = qr/ville/;

say 'Trouvé ambre en ville !'
if $nom =~ /$ambre$ville/;

like( $nom, qr/$ambre$ville/,
'Trouvé ambre en ville !' );

Comme is , avec like en plus

La fonction like du module Test::More teste si le premier argument correspond à l'expression régulière fournie comme second argument.

VII-C - Quantificateurs

Les expressions régulières deviennent plus puissantes grâce à l'utilisation des quantificateurs de regex. Ces métacaractères gèrent le nombre de fois où un composant de regex peut apparaître dans une chaîne correspondante. Le plus simple est ?, le quantificateur zéro ou un :

my $chat_ou_cat = qr/ch?at/;

like( 'chat', $chat_ou_cat, "'chat' reconnaît /ch?at/" );
like( 'cat',  $chat_ou_cat, "'cat' reconnaît /ch?at/"  );

Un atome quelconque d'une expression régulière suivi par le caractère ? signifie « correspond à zéro ou un de cet atome ». Cette expression régulière retourne la valeur vraie si zéro ou un caractère h suivent immédiatement le caractère c et précède immédiatement les caractères at. Cette regex reconnaît les deux sous-chaînes littérales chat et cat.

Le quantificateur un ou plusieurs, +, fait correspondre au moins l'un des atomes quantifiés :

my $quelques_a = qr/cha+t/;

like( 'chat',    $quelques_a, "'chat' reconnaît /cha+t/" );
like( 'chaat',   $quelques_a, "'chaat' correspond"       );
like( 'chaaat',  $quelques_a, "'chaaat' correspond"      );
like( 'chaaaat', $quelques_a, "'chaaaat' correspond"     );

unlike( 'cht',   $quelques_a, "'cht' ne correspond pas"  );

Il n'y a pas de limite théorique au nombre maximum d'atomes quantifiés qui peuvent correspondre.

Le quantificateur zéro, un ou plusieurs, *, cherche à reconnaître zéro, une ou plusieurs instances de l'atome quantifié :

my $tout_a = qr/cha*t/;

like( 'chat',    $tout_a, "'chat' reconnaît /cha*t/" );
like( 'chaat',   $tout_a, "'chaat' correspond"       );
like( 'chaaat',  $tout_a, "'chaaat' correspond"      );
like( 'chaaaat', $tout_a, "'chaaaat' correspond"     );
like( 'cht',     $tout_a, "'cht' correspond"         );

Cela peut paraître ridicule, mais ça vous permet de spécifier les composants optionnels d'une expression régulière. Utilisez-le pourtant avec parcimonie : c'est un outil brutal et coûteux. La plupart des expressions régulières bénéficient de l'utilisation des quantificateurs ? et + beaucoup plus que de l'utilisation de *. Préciser l'intention améliore souvent la clarté.

Les quantificateurs numériques expriment le nombre de fois où un atome peut correspondre. {n} signifie qu'une correspondance doit avoir lieu exactement n fois.

# équivalent à qr/chat/;
my $un_seul_a = qr/cha{1}t/;

like( 'chat', $un_seul_a, "'chat' reconnaît /cha{1}t/" );

{n,} recherche la correspondance d'un atome au moins n fois :

# équivalent à qr/cha+t/;
my $quelques_a = qr/cha{1,}t/;

like( 'chat',    $quelques_a, "'chat' correspond à /cha{1,}t/" );
like( 'chaat',   $quelques_a, "'chaat' correspond"             );
like( 'chaaat',  $quelques_a, "'chaaat' correspond"            );
like( 'chaaaat', $quelques_a, "'chaaaat' correspond"           );

{n,m} signifie qu'une correspondance doit être trouvée au moins n fois et pas plus de m fois :

my $certains_a = qr/cha{1,3}t/;

like( 'chat',    $certains_a, "'chat' correspond à /cha{1,3}t/" );
like( 'chaat',   $certains_a, "'chaat' correspond"              );
like( 'chaaat',  $certains_a, "'chaaat' correspond"             );

unlike( 'chaaaat', $certains_a, "'chaaaat' ne correspond pas"   );

Vous pouvez exprimer les quantificateurs symboliques (?, + et *) sous la forme de quantificateurs numériques, mais les quantificateurs symboliques sont plus courts et sont utilisés plus souvent. Par exemple, l'expression régulière /a?/ est synonyme de /a{0,1}/, de même que /b+/ est synonyme de /b{1,}/ et que l'expression /c*/ est équivalente à /c{0,}/, mais c'est généralement la première forme qui sera préférée dans chaque cas.

VII-D - Gourmandise

Les quantificateurs + et * sont gourmands : ils essaient de trouver dans la chaîne d'entrée autant de correspondances que possible. C'est particulièrement pernicieux. Examinez une utilisation naïve du motif « zéro ou plusieurs caractères non-saut de ligne » de .* :

# une regex faillible
my $ciel_bleu = qr/ciel.*bleu/;

say 'Il fait beau !'
if 'Le ciel est bleu' =~ $ciel_bleu;

say 'Il fait beau !'
if 'logiciel, cordon-bleu!' =~ $ciel_bleu;

Les quantificateurs gourmands commencent par tout reconnaître au début. Si cela ne réussit pas, le moteur de regex reculera d'un caractère à la fois jusqu'à ce qu'il trouve une correspondance (ou finisse par échouer).

Le modificateur de quantificateur ? transforme un quantificateur gourmand en non gourmand :

my $gourmandise_minimale = qr/ciel.*?bleu/;

Avec un quantificateur non gourmand, le moteur d'expression régulière préférera la correspondance la plus courte possible. Si cette correspondance échoue, le moteur va augmenter d'un caractère à la fois le nombre de caractères identifiés par la combinaison .*?. Comme * recherche une correspondance de zéro à plusieurs fois, la correspondance potentielle minimale pour cette combinaison est de zéro caractère :

say 'Il fait beau'
if 'beaucielbleu' =~ /$gourmandise_minimale/;

Utilisez +? pour rechercher la correspondance d'une ou plusieurs sous-chaînes de façon non gourmande :

my $gourmandise_minimale_plus = qr/ciel.+?bleu/;

unlike( 'beaucielbleu',  $gourmandise_minimale_plus );

like( 'beau ciel bleu',  $gourmandise_minimale_plus );

Le modificateur de quantificateur ? s'applique aussi au quantificateur ? (zéro ou une correspondance) ainsi qu'aux quantificateurs d'intervalle. Il provoque toujours la correspondance minimale entre l'expression régulière et la chaîne en entrée.

Les regex sont puissantes, mais elles ne sont pas toujours la meilleure façon de résoudre un problème. C'est doublement vrai pour les motifs gourmands .+ et .*. Un amateur de mots croisés qui doit remplir un mot de cinq lettres, commençant par h et finissant par s, pour la définition« sol riche ») trouvera beaucoup trop de candidats non valides pour le motif :

my $sept_vertical = qr/h$uniquement_lettres*s/;

Si la recherche est exécutée parmi tous les mots d'un dictionnaire, les correspondances antihéros, Bahamas et Chartres seront trouvées longtemps avant d'atteindre la réponse correcte humus. Non seulement ces mots sont trop longs, mais les correspondances commencent au milieu des mots.

VII-E - Ancres de regex

Il est important de savoir comment le moteur d'expressions régulières gère les correspondances gourmandes, mais c'est tout aussi important de savoir quel genre de correspondances vous voulez et vous ne voulez pas. Les ancres de regex forcent le moteur de regex à commencer ou finir une correspondance à une position fixe. L'ancre de début de chaîne (\A) stipule que toute correspondance doit commencer dès le début de la chaîne :

# correspond aussi à "haïssable", "hexose" et "huissier"
my $sept_vertical = qr/\Ah${uniquement_lettres}{3}s/;

L'ancre de fin de chaîne (\z) exige la fin de la correspondance exactement à la fin de la chaîne.

# correspond aussi à "haras", mais une amélioration évidente
my $sept_vertical = qr/\Ah${letters_only}{3}s\z/;

Vous verrez souvent les assertions ^ et $ utilisées pour trouver des correspondances au début et à la fin des chaînes. ^ trouve une correspondance au début de la chaîne, mais dans certaines circonstances avec des chaînes multilignes, il peut trouver une correspondance dans la chaîne juste après un saut de ligne. De même, $ trouve une correspondance à la fin de la chaîne (juste avant un saut de ligne, s'il existe), mais elle peut trouver une correspondance juste avant un saut de ligne au milieu de la chaîne. \A et \z sont plus spécifiques et, par conséquent, plus utiles.

L'ancre de début ou fin de mot (\b) correspond uniquement à la limite entre un caractère de mot (\w) et un caractère de non-mot (\W). Cette délimitation n'est pas un caractère en soi ; elle n'a pas de largeur. Elle est invisible. Utilisez une regex ancrée pour trouver humus en interdisant Bahamas :

my $sept_vertical = qr/\bh${uniquement_lettres}{3}s\b/;

VII-F - Métacaractères

Perl interprète plusieurs caractères dans des expressions régulières comme des métacaractères, des caractères représentant autre chose que leur interprétation littérale. Vous avez vu déjà quelques-uns de ces métacaractères (\b, . et ?, par exemple). Les métacaractères donnent aux utilisateurs des regex une puissance bien au-delà de simples correspondances de sous-chaînes. Le moteur de regex traite tous les métacaractères comme atomes.

Le métacaractère . signifie « correspond à tout caractère sauf un saut de ligne ». Beaucoup de débutants oublient cette nuance. Une recherche de regex simple  ignorant l'amélioration évidente de l'utilisation d'ancres  pour notre problème de mots croisés pourrait être /h...s/. Bien sûr, il y a toujours plus d'une façon d'obtenir la bonne réponse :

for my $mot (@mots)
{
next unless length( $mot ) == 5;
next unless $mot =~ /h...s;
say "Possibilité : $mot";
}

Si les correspondances possibles dans @mots sont plus que de simples mots, vous obtiendrez de faux positifs. Le métacaractère point . reconnaît aussi des caractères de ponctuation, des espaces et des nombres. Soyez précis ! Le métacaractère \w représente tous les caractères alphanumériques (Unicode et chaînes de caractères) et le trait de soulignement :

next unless $mot =~ /h\w\ws/;

Le métacaractère \d recherche des correspondances des chiffres (aussi dans le sens Unicode) :

# un comparateur non robuste de numéros de téléphone
next unless $numero =~ /\d{3}-\d{3}-\d{4}/;
say "Votre numéro : $numero";

Utilisez le métacaractère \s pour rechercher la correspondance d'un espace. L'espace signifie un espace littéral, un caractère de tabulation, un retour de chariot, un saut de page ou un saut de ligne :

my $deux_mots_de_trois_lettres = qr/\w{3}\s\w{3}/;

Négations des métacaractères

Ces métacaractères ont des formes négatives. Utilisez \W pour trouver la correspondance de n'importe quel caractère sauf un caractère alphabétique. \D permet de rechercher des caractères non numériques. Utilisez \S pour rechercher la correspondance d'autre chose qu'un espace. Utilisez \B pour rechercher une correspondance n'importe où, sauf à une limite de mot.

VII-G - Classes de caractères

Si aucun de ces métacaractères n'est assez spécifique, vous pouvez créer vos propres groupes de caractères en classes de caractères en les plaçant entre crochets. Une classe de caractères vous permet de traiter un groupe d'options comme un seul atome.

my $voyelles_ascii = qr/[aeiouy]/;
my $possible_chat  = qr/ch${voyelles_ascii}t/;

L'interpolation arrive

Sans ces accolades, l'analyseur de Perl interpréterait le nom de la variable comme $voyelles_asciit, ce qui soit provoque une erreur de compilation au sujet d'une variable inconnue, soit interpole le contenu d'une $voyelles_asciit existante dans l'expression rationnelle.

Le tiret (-) vous permet d'inclure une plage contiguë de caractères dans une classe, comme cette regex $uniquement_lettres_ascii :

my $uniquement_lettres_ascii = qr/[a-zA-Z]/;

Pour inclure le tiret comme membre de la classe, utilisez-le au début ou à la fin de la classe :

my $ponctuation_interessante = qr/[-!?]/;

... ou protégez-le par une barre oblique inverse (ou antislash) :

my $caracteres_ligne = qr/[|=\-_]/;

L'utilisation de l'accent circonflexe (^) comme premier élément de la classe de caractères signifie « toute autre chose à l'exception de ces caractères » :

my $pas_une_voyelle_ascii = qr/[^aeiouy]/;

Utilisez un accent circonflexe n'importe où, sauf à la première position pour en faire un membre de la classe de caractères. Pour inclure un tiret dans une classe de caractères niée, soit placez-le après l'accent circonflexe ou à la fin de la classe, soit protégez-le par un antislash.

VII-H - Capturer

Les expressions régulières vous permettent de grouper et de capturer des portions de correspondance pour une utilisation ultérieure. Pour extraire un numéro américain de téléphone de la forme (202) 456-1111 à partir d'une chaîne :

my $code_zone        = qr/\(\d{3}\)/;
my $numero_local     = qr/\d{3}-?\d{4}/;
my $numero_telephone = qr/$code_zone\s?$numero_local/;

Remarquez en particulier l'échappement des parenthèses dans $code_zone. Les parenthèses sont spéciales dans les expressions régulières Perl. Elles groupent des atomes en unités plus grandes et en même temps capturent des portions de correspondance de chaînes. Pour rechercher des correspondances des parenthèses littérales, protégez-les avec des antislash comme dans $code_zone.

VII-H-1 - Captures nommées

Perl 5.10 a ajouté les captures nommées, qui vous permettent de capturer des portions de correspondance en appliquant une expression régulière et d'y accéder plus tard. Par exemple, lors de l'extraction d'un numéro de téléphone à partir des informations de contact :

if ($contact_info =~ /(?<telephone>$numero_telephone)/)
{
say "Trouvé un numéro $+{telephone}";
}

Les regex ont tendance à ressembler à une soupe à la ponctuation jusqu'au moment où vous pouvez regrouper les diverses parties en morceaux. La syntaxe de capture nommée présente la forme :

(?<nom_capture> ... )

Des parenthèses entourent la capture. La construction ?<nom> suit immédiatement la parenthèse ouverte et fournit un nom pour cette capture spécifique. Le reste de la capture est une expression régulière.

Lorsqu'une correspondance au motif inclus réussit, Perl met à jour la variable magique %+. Dans ce hachage, la clé est le nom de la capture et la valeur est la portion de la chaîne correspondant à la capture.

VII-H-2 - Captures numérotées

Perl met en œuvre depuis très longtemps des captures numérotées :

if ($contact_info =~ /($numero_telephone)/)
{
say "Trouvé un numéro $1";
}

Cette forme de capture ne fournit aucun nom d'identification et ne fait rien à %+. Au lieu de cela, Perl stocke ici la sous-chaîne capturée dans une série de variables magiques. La première capture correspondante trouvée par Perl va dans $1, la deuxième dans $2 et ainsi de suite. Le comptage des captures commence à la parenthèse ouvrante de la capture. Ainsi, la première parenthèse gauche commence la capture dans $1, la seconde dans $2, et ainsi de suite.

La syntaxe pour les captures nommées est plus longue que pour les captures numérotées, mais elle offre plus de clarté. Compter les parenthèses gauches est un travail fastidieux et combiner les expressions régulières qui contiennent des captures numérotées est difficile. Les captures nommées améliorent la maintenabilité des regex  bien que les collisions de noms soient possibles, elles sont relativement rares. Minimisez les risques en utilisant les captures nommées uniquement dans les expressions régulières finales, mais pas dans les « briques » servant à assembler des expressions régulières plus complexes.

Dans un contexte de liste, une correspondance de regex retourne une liste de sous-chaînes capturées :

if (my ($numero) = $contact_info =~ /($numero_telephone)/)
{
say "Trouvé un numéro $numero";
}

Les captures numérotées sont également utiles lors des substitutions simples, où les captures nommées peuvent être plus verbeuses :

my $commande = 'Des brownies Végan !';

$commande =~ s/Végan (\w+)/Végétarien $1/;
# ou
$commande =~ s/Végan (?<aliment>\w+)/Végétarien $+{aliment}/;

VII-I - Regroupement et alternatives

Les exemples précédents ont tous appliqué des quantificateurs à des atomes simples. Vous pouvez les appliquer à n'importe quel élément d'une regex :

my $porc     = qr/porc/;
my $haricots = qr/haricots/;

like( 'porc aux haricots', qr/\A$porc?.*?$haricots/,
'peut-être porc, certainement haricots' );

Si vous étendez manuellement l'expression régulière, les résultats peuvent vous surprendre :

my $porc_aux_haricots = qr/\Aporc?.*haricots/;

like( 'porc aux haricots', qr/$porc_aux_haricots/,
'peut-être porc, certainement haricots'     );
like( 'por aux haricots', qr/$porc_aux_haricots/,
'attendez... pas d\'acide ascorbique ici !' );

La spécificité permet parfois de préciser le motif :

my $porc     = qr/porc/;
my $aux      = qr/aux/;
my $haricots = qr/haricots/;

like( 'porc aux haricots', qr/\A$porc? $aux? $haricots/,
'peut-être porc, peut-être aux, certainement haricots' );

Certaines expressions régulières doivent correspondre soit à une chose, soit à l'autre. Le métacaractère d'alternative (|) indique qu'une des deux possibilités peut correspondre.

my $riz      = qr/riz/;
my $haricots = qr/haricots/;

like( 'riz',      qr/$riz|$haricots/, 'Trouvé riz'      );
like( 'haricots', qr/$riz|$haricots/, 'Trouvé haricots' );

Alors qu'il est facile à interpréter riz|haricots comme ri, suivi soit par z, soit par h, suivi par aricots, les alternatives incluent toujours l'intégralité du fragment jusqu'au délimiteur de regex le plus proche, que ce soit le début ou la fin du motif, une parenthèse fermante, un autre caractère d'alternative ou un crochet.

L'alternative a une priorité inférieure (Ordre des opérations (priorité)) aux atomes mêmes :

like(   'riz',      qr/riz|haricots/, 'Trouvé riz'      );
like(   'haricots', qr/riz|haricots/, 'Trouvé haricots' );
unlike( 'rih',      qr/riz|haricots/, 'Trouvé hybride'  );

Pour éviter la confusion, utilisez des fragments nommés dans des variables ($riz|$haricots) ou groupez les candidats d'alternative dans des groupes non capturants :

my $feculents = qr/(?:pates|patates|riz)/;

La séquence (?:) regroupe une série d'atomes sans faire une capture.

Non-capture pour votre protection

Une expression régulière transformée en chaîne contient un groupe englobant sans capture ; qr/riz/haricots/ devient (?^u:riz|haricots).

VII-J - Autres séquences d'échappement

Pour faire correspondre l'instance littérale d'un métacaractère, préfixez-le avec le caractère d'échappement, l'antislash (\). Vous avez déjà vu cela auparavant, là où \( désigne une seule parenthèse gauche et \] se réfère à un seul crochet fermant. \. désigne un caractère point littéral et non l'atome « faire correspondre tout sauf un caractère explicite de saut de ligne ».

Vous aurez probablement besoin d'échapper le métacaractère d'alternative (|) ainsi que le métacaractère de fin de ligne ($) et les quantificateurs (+, ?, *).

Les caractères de désactivation de métacaractères (\Q et \E) désactivent l'interprétation des métacaractères à l'intérieur de leurs limites. Ceci est particulièrement utile lors de la correspondance du texte provenant d'une source que vous ne contrôlez pas :

my ($text, $literal_text) = @_;

return $text =~ /\Q$literal_text\E/;

L'argument $literal_text peut contenir n'importe quoi  la chaîne ** ALERTE **, par exemple. Dans le fragment délimité par \Q et \E, Perl va interpréter l'expression régulière comme \*\* ALERTE \*\* et tentera de faire correspondre les caractères astérisque littéraux au lieu de les traiter comme quantificateurs gourmands.

Sécurité des expressions régulières

Soyez prudents lors du traitement des expressions régulières provenant de la saisie de l'utilisateur non fiable. Un maître des regex malveillant pourrait concevoir une expression régulière qui prendrait des années pour trouver des correspondances dans la chaîne de caractères en entrée, créant une attaque par déni de service contre votre programme.

VII-K - Assertions

Les ancres de regex telles que \A, \b, \B et \Z sont une forme d'assertion qui exige que la chaîne satisfasse à une certaine condition. Ces assertions ne correspondent pas à des caractères individuels dans la chaîne. Peu importe ce que la chaîne contient, l'expression régulière qr/\A/ correspondra toujours.

Les assertions de largeur nulle correspondent à un motif. Surtout, elles ne consomment pas la partie du motif qu'elles reconnaissent. Par exemple, pour trouver un seul chat, vous pouvez utiliser une assertion de fin de mot :

my $juste_un_chat = qr/cat\b/;

… mais si vous voulez trouver un félin non désastreux, vous pourriez utiliser une assertion négative avant de largeur nulle :

my $felin_prudent = qr/cat(?!astrophe)/;

La construction (?!...) correspond au motif cat uniquement si le motif astrophe ne suit pas immédiatement. L'assertion positive arrière de largeur nulle :

my $felin_desastreux = qr/cat(?=astrophe)/;

... correspond à l'expression cat uniquement si l'expression astrophe suit immédiatement. Lorsqu'une expression régulière normale peut accomplir la même chose, envisagez une regex pour trouver dans le dictionnaire tous les mots non catastrophiques qui commencent par cat :

my $felin_desastreux = qr/cat(?!astrophe)/;

while (<$mots>)
{
chomp;
next unless /\A(?<cat>$felin_desastreux.*)\Z/;
say "Trouvé un '$+{cat}' non-catastrophique";
}

L'assertion de largeur nulle ne consomme rien de la chaîne source, laissant le fragment ancré <.*\Z> rechercher la correspondance. Dans le cas contraire, la capture capturerait uniquement la partie cat de la chaîne source.

Pour affirmer que votre félin ne se produit jamais au début d'une ligne, vous pouvez utiliser une assertion négative arrière de largeur nulle. Ces assertions doivent avoir des tailles fixes. Vous ne pouvez pas utiliser les quantificateurs :

my $milieu_chat = qr/(?<!\A)chat/;

La construction (?<!...) contient le motif de largeur fixe. Vous pouvez également exprimer que cat doit toujours se trouver immédiatement après un caractère d'espace avec une assertion positive arrière de largeur nulle :

my $espace_chat = qr/(?<=\s)chat/;

La construction (?<=...) contient le motif de largeur fixe. Cette approche peut être utile pour combiner une correspondance globalede regex avec le modificateur \G.

Une nouvelle fonctionnalité des regex Perl est l'assertion keep \K. Cette assertion positive arrière de largeur nulle peut avoir une longueur variable :

my $espacee_chat = qr/\s+\Kchat/;

like( 'mon chat a été dans l\'espace', $espacee_chat );
like( 'mon  chat  a  été  dans  un  double-espace',
$espacee_chat );

\K est étonnamment utile pour certaines substitutions qui éliminent la fin d'un motif. Il vous permet de rechercher un motif, mais de supprimer seulement une partie de celui-ci :

my $exclamation = 'Ceci est un désastre!';
$exclamation    =~ s/dé\K\w+!/./;
# $exclamation vaut maintenant : 'Ceci est un dé!'

like( $exclamation, qr/\bdé\./,
"Pas si mal que ça !" );

Tout jusqu'à l'assertion \K correspond, mais seulement la partie qui suit l'assertion est substituée.

VII-L - Modificateurs de regex

Plusieurs modificateurs changent le comportement des opérateurs d'expression régulière. Ces modificateurs apparaissent à la fin des opérateurs de correspondance, substitution et qr//. Par exemple, pour activer la correspondance insensible à la casse (la différence entre les lettres capitales et minuscules) :

my $animal = 'CaMeLiA';

like( $animal, qr/Camelia/,  'Joli papillon !'        );
like( $animal, qr/Camelia/i, 'touche Maj défectueuse' );

La première like() échouera, car les chaînes contiennent des lettres différentes. La seconde like() passera, parce que le modificateur /i dit à la regex d'ignorer les distinctions de casse. M et m sont équivalents dans la seconde regex en raison du modificateur.

Vous pouvez également intégrer des modificateurs de regex dans un motif :

my $trouver_chat = qr/(?<felin>(?i)chat)/;

La syntaxe (?i) active la correspondance insensible à la casse uniquement pour le groupe qu'elle englobe. Dans ce cas, c'est la capture nommée. Vous pouvez utiliser plusieurs modificateurs avec cette forme. Désactivez les modificateurs spécifiques en les faisant précéder par le caractère moins (-) :

my $trouver_nombre_rationnel = qr/(?<nombre>(?-i)Rat)/;

Le modificateur multiligne, /m, permet aux ancres ^ et $ de correspondre à n'importe quel saut de ligne présent dans la chaîne.

Le modificateur /s traite la chaîne source comme une seule ligne de sorte que le métacaractère . reconnaisse aussi le caractère de saut de ligne. Damian Conway suggère la mnémonique suivante : /m modifie le comportement de multiples métacaractères des expressions régulières, tandis que /s modifie le comportement d'un seul métacaractère de la regex.

Le modificateur /r provoque une opération de substitution et retourne le résultat de celle-ci, en laissant inchangée la chaîne d'origine. Si la substitution réussit, le résultat est une copie modifiée de l'original. Si la substitution échoue (parce que le motif ne correspond pas), le résultat est une copie non modifiée de l'original :

my $statut          = 'Envie d\'une tarte.';
my $nouveau_statut  = $statut =~ s/tarte/gateau/r;
my $copie_statut    = $statut
=~ s/foie aux oignons/bratwurst/r;

is( $statut, 'Envie d\'une tarte.',
'la chaîne originale doit être inchangée' );

like( $nouveau_statut,   qr/gateau/,    'gâteau souhaité' );
unlike( $copie_statut,   qr/bratwurst/, 'pas de saucisse' );

Le modificateur /x permet d'inclure des espaces supplémentaires et des commentaires dans les motifs. Avec ce modificateur activé, le moteur de regex ignore les espaces et les commentaires, alors votre code peut être plus lisible :

my $attr_re = qr{
\A                    # début de ligne

(?:
[;\n\s]*            # espaces et points-virgules
(?:/\*.*?\*/)?      # commentaires en C
)*

ATTR

\s+
(   U?INTVAL
| FLOATVAL
| STRING\s+\*
)
}x;

Cette regex n'est pas simple, mais les commentaires et les espaces améliorent sa lisibilité. Même si vous fusionnez des regex compilées à partir de fragments, le modificateur /x peut encore améliorer votre code.

Le modificateur /g correspond à une expression régulière globale d'une chaîne. Cela a un sens, lorsqu'il est utilisé avec une substitution :

# apaiser la succession de Margaret Mitchell
my $contents = slurp( $file );
$contents    =~ s/Scarlett O'Hara/Mauve Midway/g;

Lorsqu'il est utilisé avec une correspondance  pas avec une substitution, le métacaractère \G permet de traiter une chaîne dans une boucle, un morceau à la fois. \G correspond à la position où la correspondance la plus récente a pris fin. Pour traiter un fichier mal codé contenant des numéros de téléphone américains en morceaux logiques, vous pourriez écrire :

while ($contient =~ /\G(\w{3})(\w{3})(\w{4})/g)
{
push @numeros, "($1) $2-$3";
}

Sachez que l'ancre \G commencera au dernier point dans la chaîne où loccurrence d'une correspondance s'est produite dans l'itération précédente. Si l'occurrence précédente s'est terminée par une correspondance gourmande telle que .*, il restera moins de caractères disponibles dans la chaîne pour trouver une correspondance lors de la prochaine itération. Les assertions avant peuvent également aider.

Le modificateur /e vous permet d'écrire du code arbitraire du côté droit d'une substitution. Si la correspondance réussit, le moteur de regex exécutera le code, en utilisant sa valeur de retour comme valeur de substitution. L'exemple de substitution globale de tout à l'heure pourrait être plus simple avec le code suivant :

# apaiser la succession de Margaret Mitchell
$suite  =~ s{Scarlett( O'Hara)?}
{
'Mauve' . defined $1
? ' Midway'
: ''
}ge;

Chaque occurrence supplémentaire du modificateur /e provoquera une autre évaluation du résultat de l'expression, bien que seuls les experts Perl utilisent quelque chose au-delà de /ee.

VII-M - Reconnaissances intelligentes

L'opérateur de reconnaissance intelligente, ~~, compare deux opérandes et renvoie la valeur vraie s'il trouve une correspondance. Le type de comparaison dépend du type de deux opérandes. La construction given (Instructions switch) effectue une reconnaissance intelligente implicite.

À partir de Perl 5.18, cette fonctionnalité est expérimentale. Les détails de la conception actuelle sont complexes et difficiles à manier et aucune proposition de simplification des choses n'a gagné un soutien populaire suffisant pour justifier une révision complète. Plus vos opérandes sont complexes, plus vous êtes susceptible de recevoir des résultats déroutants. Évitez de comparer des objets et tenez-vous à des opérations simples entre deux scalaires ou un scalaire et une variable composite pour obtenir les meilleurs résultats.

L'opérateur de reconnaissance intelligente est un opérateur infixé :

say 'Ils correspondent (en quelque sorte)' if $operand_gauche ~~
$operand_droit;

Le type de comparaison dépend généralement d'abord du type de l'opérande droit, puis de l'opérande gauche. Par exemple, si l'opérande droit est un scalaire avec un composant numérique, la comparaison utilisera l'égalité numérique. Si l'opérande droit est une expression régulière, la comparaison utilisera un grep ou une correspondance au motif. Si l'opérande droit est un tableau, la comparaison effectuera un grep ou une reconnaissance intelligente récursive. Si l'opérande droit est un hachage, la comparaison vérifiera l'existence d'une ou plusieurs clés. Un diagramme grand et intimidant dans perldoc perlsyn donne beaucoup plus de détails sur toutes les comparaisons que la reconnaissance intelligente peut effectuer.

Ces exemples sont volontairement simples, parce que la reconnaissance intelligente peut être source de confusion :

my ($x, $y) = (10, 20);
say 'Inégalité numérique' unless $x ~~ $y;

my $z = '10 petits indiens';
say 'Égalité numérique' if $x ~~ $z;

my $aiguille = qr/aiguille/;

say 'Correspondance' if 'aiguille' ~~ $aiguille;

say 'Grep dans un tableau' if @botte_de_foin ~~ $aiguille;

say 'Grep des clés de hachage' if %hachage_de_foin ~~ $aiguille;

say 'Grep dans un tableau' if $aiguille ~~ @botte_de_foin;

say 'Les éléments du tableau existent en tant que clés de hachage'
if %hachage_de_foin ~~ @botte_de_foin;

say 'Reconnaissance intelligente des éléments' if @paille ~~ @botte_de_foin;

say 'Grep des clés de hachage' if $aiguille ~~ %hachage_de_foin;

say 'Les éléments du tableau existent en tant que clés de hachage'
if @botte_de_foin ~~ %hachage_de_foin;

say 'Clés de hachage identiques' if %hachage_de_foin ~~ %foinmap;

La reconnaissance intelligente fonctionne même si l'un des opérandes est une référence au type de données donné :

say 'Clés de hachage identiques' if %hachage_de_foin ~~ \%foinmap;

Il est difficile de conseiller l'utilisation de la reconnaissance intelligente, sauf dans les circonstances les plus simples, mais elle peut être utile lorsque vous devez trouver une correspondance entre une chaîne littérale ou un nombre et une variable.

VIII - Objets

Lorsque vous concevez un programme, vous devez l'aborder aux nombreux niveaux. À la base, vous avez des détails spécifiques sur le problème que vous résolvez. Aux niveaux supérieurs, vous devez organiser le code pour qu'il ait du sens. Notre seul espoir pour gérer cette complexité est d'exploiter l'abstraction (traiter des choses semblables de façon semblable) et l'encapsulation (regrouper les détails connexes).

À elles seules, les fonctions sont insuffisantes pour les problèmes complexes. Plusieurs techniques groupent les problèmes en unités de comportements semblables. Une technique populaire est l'orientation objet (OO), ou la programmation orientée objet (POO), où les programmes travaillent avec des objets  des entités discrètes, uniques, ayant leur propre identité.

VIII-A - Moose

Le système objet par défaut de Perl est minime, mais flexible. Sa syntaxe est un peu maladroite et révèle comment fonctionne un système objet. Vous pouvez construire de grandes choses sur celui-ci, mais vous ou quelqu'un d'autre devrez écrire beaucoup de code pour obtenir ce que certains autres langages fournissent gratuitement.

Moose est un système objet complet pour Perl. C'est une distribution complète disponible sur le CPAN et il ne fait pas partie du langage standard, mais c'est quand même intéressant de l'installer et de l'utiliser. Moose offre à la fois une façon simplifiée d'utiliser un système objet et des fonctionnalités avancées de langages tels que Smalltalk et Common Lisp.

Les objets Moose fonctionnent avec du Perl standard. Dans vos programmes, vous pouvez mélanger et assortir des objets écrits avec le système d'objets par défaut de Perl et Moose. Bien sûr, vous devrez écrire beaucoup plus de code pour obtenir ce que Moose vous fournit.

Documentation Moose

Voir Moose::Manual sur CPAN pour la documentation complète de Moose.

VIII-A-1 - Classes

Un objet Moose est une instance concrète d'une classe, qui est un modèle décrivant les données et comportements spécifiques à l'objet. Une classe appartient généralement à un paquetage (Paquetages), qui lui donne son nom :

package Chat
{
use Moose;
}

Cette classe Chat semble ne rien faire, mais c'est tout ce dont Moose a besoin pour créer une classe. Créez des objets (ou instances) de la classe Chat en utilisant la syntaxe :

my $brad = Chat->new;
my $jack = Chat->new;

Tout comme l'opérateur flèche déréférence une référence, cette flèche-ci appelle une méthode sur Chat.

VIII-A-2 - Méthodes

Une méthode est une fonction associée à une classe. Une fonction peut appartenir à un espace de noms ; vous avez vu cela. De même, une méthode appartient à une classe.

Lorsque vous appelez une méthode, vous le faites avec un invoquant. Lorsque vous appelez new() sur Chat, le nom de la classe, Chat, est linvoquant de new(). Pensez à cela comme à l'envoi d'un message à une classe : « faites ce que new() fait. » Dans ce cas, l'envoi du message new  l'appel de la méthode new()  retourne un nouvel objet de la classe Chat.

Lorsque vous appelez une méthode sur un objet, cet objet est linvoquant :

my $choco = Chat->new;
$choco->dort_sur_clavier;

Le premier argument d'une méthode est son invoquant ($self, par convention). Supposons qu'un Chat puisse miauler() :

package Chat
{
use Moose;

sub miauler
{
my $self = shift;
say 'Miaou !';
}
}

Maintenant, n'importe quelle instance de Chat peut vous réveiller le matin, parce qu'il n'a pas encore mangé :

# le chat miaule toujours trois fois à 6 heures
my $reveil_vivant = Chat->new;
$reveil_vivant->miauler for 1 .. 3;

Chaque objet peut avoir ses propres données distinctes. (Nous y reviendrons très prochainement.) Les méthodes qui lisent ou écrivent les données de leur invoquant sont des méthodes d'instance ; elles dépendent de la présence d'un invoquant approprié pour fonctionner correctement. Des méthodes (comme miauler()) qui n'accèdent pas aux données d'instance sont des méthodes de classe. Vous pouvez appeler des méthodes de classe sur des classes et des méthodes de classe et d'instance sur les instances, mais vous ne pouvez pas appeler des méthodes d'instance sur des classes.

Les constructeurs, qui créent des instances, sont évidemment des méthodes de classe. Moose vous fournit un constructeur par défaut, nommé new(). C'est pourquoi vous pouvez créer un objet Chat par la déclaration minimale de classe de tout à l'heure.

Les méthodes de classe sont effectivement des fonctions globales de l'espace de noms. N'ayant pas accès aux données de l'instance, elles ont peu d'avantages par rapport aux fonctions d'un espace de noms. La majorité du code OO utilise des méthodes d'instance pour lire et écrire des données d'instance.

VIII-A-3 - Attributs

En Perl, chaque objet est unique. Les objets peuvent contenir des données privées associées à chaque objet unique  vous pouvez entendre ces données appelées attributs, données d'instance, ou état de l'objet. Définissez un attribut en le déclarant comme élément de la classe :

package Chat
{
use Moose;

has 'nom', is => 'ro', isa => 'Str';
}

Moose exporte la fonction has() que vous pouvez utiliser pour déclarer un attribut. Ce code se lit : « les objets Chat ont un attribut nom. Cet attribut est en lecture seule et est une chaîne de caractères. » Le premier argument, 'nom', est le nom de l'attribut. La paire d'arguments is=> 'ro' déclare que cet attribut est en lecture seule, donc vous ne pouvez pas modifier la valeur de l'attribut après l'avoir définie. Enfin, la paire isa => 'Str' déclare que la valeur de cet attribut peut être exclusivement une chaîne.

Dans cet exemple, Moose crée une méthode accesseur nommée nom() et vous permet de passer un paramètre nom au constructeur de Chat :

for my $nom (qw( Tuxie Petunia Daisy ))
{
my $chat = Chat->new( nom => $nom );
say "Créé un chat nommé ", $chat->nom;
}

La documentation de Moose utilise des parenthèses pour séparer les noms des attributs des leurs caractéristiques :

has 'nom' => ( is => 'ro', isa => 'Str' );

Cela équivaut à :

has( 'nom', 'is', 'ro', 'isa', 'Str' );

L'approche de Moose fonctionne bien pour les déclarations complexes :

has 'nom' => (
is         => 'ro',
isa        => 'Str',

# options Moose avancées; perldoc Moose
init_arg   => undef,
lazy_build => 1,
);

... tandis que ce livre préfère une approche à faible ponctuation pour les déclarations simples. Choisissez le style qui vous offre le plus de clarté.

Moose se plaindra si vous essayez d'affecter à nom une valeur qui n'est pas une chaîne. Les attributs n'ont pas besoin d'avoir des types. Dans ce cas, tout passera :

package Chat
{
use Moose;

has 'nom', is => 'ro', isa => 'Str';
has 'age', is => 'ro';
}

my $invalide = Chat->new( nom => 'bizarre',
age => 'pourpre' );

Si vous ajoutez un type à votre déclaration d'attribut, Moose tentera de valider les valeurs assignées à cet attribut. Parfois, cette rigueur est inestimable.

Si vous marquez un attribut en lecture et écriture (avec is => rw), Moose créera unmutateur, c'est-à-dire une méthode qui peut changer la valeur de l'attribut :

package Chat
{
use Moose;

has 'nom',   is => 'ro', isa => 'Str';
has 'age',   is => 'ro', isa => 'Int';
has 'diete', is => 'rw';
}

my $gros = Chat->new( nom   => 'Fatty',
age   => 8,
diete => 'fruits de mer' );

say $gros->nom, ' mange ', $gros->diete;

$gros->diete( 'Low Sodium Kitty Lo Mein' );
say $gros->nom, ' mange maintenant ', $gros->diete;

Un accesseur ro utilisé comme mutateur lancera l'exception Cannot assign a value to a read-only accessor at ....

L'utilisation de ro ou rw est une question de conception, commodité et pureté. Moose n'impose aucune philosophie particulière dans ce domaine. Certaines personnes suggèrent de définir comme ro toutes les données d'instance, de sorte que vous devez les passer dans le constructeur (Immutabilité). Dans l'exemple Chat, age() pourrait être toujours un accesseur, mais le constructeur pourrait prendre l'année de naissance du chat et calculer lui-même l'âge en fonction de l'année en cours. Cette approche renforce le code de validation et assure que tous les objets créés contiennent des données valides.

Les données d'instance montrent une partie de la valeur de l'orientation objet. Un objet contient des données connexes et peut accomplir des comportements avec ces données. Une classe décrit ces données et comportements. Vous pouvez avoir plusieurs objets indépendants avec données d'instance séparées et traiter l'ensemble de ces objets de la même manière ; ils se comporteront différemment en fonction de leurs données d'instance.

VIII-A-4 - Encapsulation

Moose vous permet de déclarer quels attributs possèdent les instances de classe (un chat a un nom) ainsi que les attributs de ces attributs (vous ne pouvez pas modifier le nom d'un chat, vous ne pouvez que le lire). Moose décide lui-même comment stocker ces attributs. Vous pouvez modifier cela si vous le souhaitez, mais permettre à Moose de gérer votre stockage encourage l'encapsulation : masquer les détails internes d'un objet aux utilisateurs externes de cet objet.

Examinez un changement de la façon dont les Chat gèrent leur âge. Au lieu de passer au constructeur une valeur pour un âge, passez l'année de naissance du chat et calculez l'âge en fonction des besoins :

package Chat
{
use Moose;

has 'nom',          is => 'ro', isa => 'Str';
has 'diete',        is => 'rw';
has 'annee_naiss',  is => 'ro', isa => 'Int';

sub age
{
my $self  = shift;
my $annee = (localtime)[5] + 1900;

return $annee - $self->annee_naiss;
}
}

La syntaxe pour créer des objets Chat a certes changé, mais la syntaxe pour utiliser des objets Chat est restée la même. En dehors de Chat, age() se comporte comme auparavant. Le comment de son fonctionnement est un détail caché à l'intérieur de la classe Chat.

Compatibilité et API

Conservez l'ancienne syntaxe pour créer des objets Chat en personnalisant le constructeur généré de Chat pour permettre de passer un paramètre age. Calculez annee_naiss à partir de celui-ci. Voir perldoc Moose::Manual::Attributes.

Le calcul de l'âge présente un autre avantage. Une valeur d'attribut par défaut fera le nécessaire quand quelqu'un crée un nouvel objet Chat sans fournir une année de naissance :

package Chat
{
use Moose;

has 'nom',   is => 'ro', isa => 'Str';
has 'diete', is => 'rw', isa => 'Str';

has 'annee_naiss',
is      => 'ro',
isa     => 'Int',
default => sub { (localtime)[5] + 1900 };
}

Le mot-clé default sur un attribut prend une référence de fonction. Vous pouvez utiliser directement une valeur simple comme un nombre ou une chaîne, mais, pour quelque chose de plus complexe, utilisez une référence de fonction qui retourne la valeur par défaut pour cet attribut lors de la construction d'un nouvel objet. Si le code de création d'un objet ne passe aucune valeur pour cet attribut au constructeur, l'objet prend la valeur par défaut :

my $chaton = Chat->new( nom => 'Choco' );

... et ce chaton aura un âge de valeur 0 jusqu'à l'année prochaine.

VIII-A-4-a - Polymorphisme

L'encapsulation est utile, mais le pouvoir réel de l'orientation objet est beaucoup plus vaste. Un programme OO bien conçu peut gérer de nombreux types de données. Lorsque des classes bien conçues encapsulent les détails spécifiques des objets dans des endroits appropriés, quelque chose de curieux se produit : le code devient souvent moins spécifique.

Déplacer les détails de ce que le programme sait à propos des Chat individuels (les attributs) et ce que le programme sait que les Chat peuvent faire (les méthodes) dans la classe Chat signifie que le code qui traite des instances de Chat peut ignorer comment Chat fait ce qu'il fait.

Examinons une fonction qui affiche les détails d'un objet :

sub afficher_details
{
my $objet = shift;

say 'Mon nom est ', $objet->nom;
say 'Mon âge est ', $objet->age;
say 'Je mange ',    $objet->diete;
}

Il est évident (en contexte) que cette fonction marchera si vous lui passez un objet Chat. C'est moins évident qu'elle va faire la bonne chose pour n'importe quel objet avec les trois accesseurs appropriés, peu importe comment cet objet fournit ces accesseurs et peu importe quel type d'objet il est : Chat, Chaton ou ChatTigre. afficher_details() considère qu'un invoquant est valide s'il prend en charge trois méthodes, nom(), age() et diete(), qui ne prennent aucun argument et retournent chacune quelque chose qui peut être concaténé dans un contexte de chaîne. Vous pouvez avoir une centaine de classes différentes dans votre code, sans aucun rapport évident entre elles, mais elles pourront être utilisées dans cette fonction si elles sont conformes à ce comportement attendu.

Cette propriété est appelée polymorphisme. Cela signifie que vous pouvez remplacer un objet d'une classe par un objet d'une autre classe s'il fournit la même interface externe.

Typage du canard

Certains langages et environnements exigent que vous déclariez (ou au moins suggériez) une relation formelle entre deux classes avant d'autoriser un programme de remplacer une instance de l'une par une instance de l'autre. Perl fournit des moyens pour faire appliquer ces contrôles, mais ne les exige pas. Son système ad hoc par défaut vous permet de traiter les deux instances ayant des méthodes du même nom comme équivalentes. Certaines personnes appellent cela typage du canard, prétextant que tout objet qui peut cancaner() ou nasiller() ressemble suffisamment à un canard pour pouvoir être pris pour un canard.

Imaginez comment vous pourriez énumérer les animaux de tout un zoo sans cette fonction polymorphe. L'avantage de la généricité devrait être évident. De même, tous les détails spécifiques sur la façon de calculer l'âge d'un ocelot ou d'un poulpe peuvent appartenir à la classe concernée  là où cela importe le plus.

Bien sûr, la simple existence d'une méthode appelée nom() ou age() n'implique pas en soi le comportement de cet objet. Un objet Chien peut avoir une méthode age() qui est un accesseur de sorte que vous pouvez découvrir que $rodney a onze ans, mais $lucky en a six. Un objet Fromage pourrait avoir une méthode age() vous permettant de contrôler la durée du stockage de $cheddar pour l'affiner. age() peut être un accesseur dans une classe, mais pas dans une autre :

# quel est l'âge du chat ?
my $annees = $zeppie->age;

# stocker le fromage dans l'entrepôt pendant six mois
$fromage->age;

Il est parfois utile de savoir que fait un objet et ce que cela signifie.

VIII-A-5 - Rôles

Un rôle est une collection nommée de comportements et d'états. Voir les documents de conception P6 sur les rôles à http://design.perl6.org/S14.html et la recherche sur les traits Smalltalk à http://scg.unibe.ch/research/traits pour plus de détails. Alors qu'une classe organise des comportements et d'états dans un modèle pour objets, un rôle organise une collection nommée de comportements et d'états. Vous pouvez instancier une classe, mais pas un rôle. Un rôle est quelque chose qu'une classe fait.

Étant donné un Animal qui a un âge et un Fromage qui peut vieillir, une différence peut être que l'Animal a le rôle EtreVivant, tandis que le Fromage a le rôle Stockable :

package EtreVivant
{
use Moose::Role;

requires qw( nom age diete );
}

Le mot-clé requires fourni par Moose::Role vous permet de répertorier les méthodes que ce rôle requiert dans ses classes composant. Autrement dit, tout ce que ce rôle doit faire est de fournir les méthodes nom(), age() et diete(). La classe Chat doit marquer explicitement le fait qu'elle remplit le rôle :

package Chat
{
use Moose;

has 'nom',   is => 'ro', isa => 'Str';
has 'diete', is => 'rw', isa => 'Str';

has 'annee_naiss',
is      => 'ro',
isa     => 'Int',
default => sub { (localtime)[5] + 1900 };

with 'EtreVivant';

sub age { ... }
}

La ligne qui commence par le mot-clé with oblige Moose à composer le rôle EtreVivant dans la classe Chat. La composition garantit que tous les attributs et les méthodes du rôle font partie de la classe. EtreVivant exige à toute classe composante de fournir des méthodes nommées nom(), age() et diete(). Chat respecte ces contraintes. Si EtreVivant était composé dans une classe qui ne fournit pas ces méthodes, Moose lancerait une exception.

L'ordre compte !

Le mot-clé with appliquant des rôles à une classe doit apparaître après la déclaration des attributs afin que la composition puisse identifier tous les accesseurs générés.

Maintenant, toutes les instances de Chat retourneront la valeur vraie lorsqu'on leur demandera si elles fournissent le rôle EtreVivant. Les objets Fromage ne le doivent pas :

say 'Vivant !' if $fluffy->DOES('EtreVivant');
say 'Moisi !'  if $fromage->DOES('EtreVivant');

Cette technique de conception sépare les capacités des classes et des objets de la mise en œuvre de ces mêmes classes et objets. Le comportement de calcul de l'année de naissance de la classe Chat pourrait être lui-même un rôle :

package CalculerAge::De::AnneeNaissance
{
use Moose::Role;

has 'annee_naiss',
is      => 'ro',
isa     => 'Int',
default => sub { (localtime)[5] + 1900 };

sub age
{
my $self  = shift;
my $annee = (localtime)[5] + 1900;

return $annee - $self->annee_naiss;
}
}

Sortir ce rôle de la classe Chat rend le comportement disponible à d'autres classes. Maintenant, Chat peut composer les deux rôles :

package Chat
{
use Moose;

has 'nom',   is => 'ro', isa => 'Str';
has 'diete', is => 'rw';

with 'EtreVivant',
'CalculerAge::De::AnneeNaissance';
}

Remarquez comment la méthode age() de CalculerAge::De::AnneeNaissance satisfait les exigences du rôle EtreVivant. Notez également que pour toute vérification effectuée par Chat, EtreVivant retourne une valeur vraie. Extraire age() dans un rôle a changé uniquement les détails du comment Chat calcule un âge. C'est toujours un EtreVivant. Chat peut choisir de mettre en œuvre son propre âge ou l'obtenir ailleurs. Tout ce qui compte c'est qu'il fournit une méthode age() qui satisfait la contrainte de EtreVivant.

Vous souvenez-vous comment le polymorphisme signifie que vous pouvez traiter de la même manière plusieurs objets ayant le même comportement ? Un objet peut mettre en œuvre le même comportement de multiples façons. C'est l'allomorphisme. Généraliser l'allomorphisme peut réduire la taille de vos classes et augmenter le code partagé entre elles. Il vous permet également de nommer des collections spécifiques et distinctes de comportements  très utile pour tester les capacités plutôt que les implémentations.

Pour comparer des rôles à d'autres techniques de conception telles que les mixins, l'héritage multiple et le monkeypatching, voir http://www.modernperlbooks.com/mt/2009/04/the-why-of-perl-roles.html.

VIII-A-5-a - Rôles et DOES()

Lorsque vous composez un rôle dans une classe, la classe et ses instances retourneront la valeur vraie lorsque vous appelez DOES() sur elles :

say 'Ce Chat est vivant !'
if $chaton->DOES( 'EtreVivant' );

VIII-A-6 - Héritage

Le système objet de Perl prend en charge l'héritage, qui établit une relation parent-enfant entre deux classes, tel que l'une spécialise l'autre. La classe enfant se comporte de la même manière que son parent  elle a le même nombre et type d'attributs et peut utiliser les mêmes méthodes. Elle peut avoir des données et des comportements supplémentaires, mais vous pouvez utiliser n'importe quelle instance d'un enfant là où le code attend son parent. Dans un sens, une sous-classe fournit le rôle impliqué par l'existence de sa classe parente.

Rôles versus héritage

Devriez-vous utiliser des rôles ou l'héritage ? Les rôles offrent la sécurité au moment de la composition, une meilleure vérification de type, une meilleure factorisation du code et un contrôle plus fin sur les noms et comportements, mais l'héritage est plus familier aux développeurs expérimentés d'autres langages. Utilisez l'héritage lorsqu'une classe en étend vraiment une autre. Utilisez un rôle lorsqu'une classe a besoin d'un comportement supplémentaire et quand vous pouvez donner à ce comportement un nom significatif.

Considérez une classe SourceDeLumiere qui fournit deux attributs publics (active et puissance_en_bougies) et deux méthodes (allumer et éteindre) :

package SourceDeLumiere {
use Moose;

has 'puissance_en_bougies',
is      => 'ro',
isa     => 'Int',
default => 1;

has 'active',
is      => 'ro',
isa     => 'Bool',
default => 0,
writer  => '_set_active';

sub allumer {
my $self = shift;
$self->_set_active(1);
}

sub eteindre {
my $self = shift;
$self->_set_active(0);
}
}

(Notez que l'option writer de active crée un accesseur privé, utilisable dans la classe pour définir la valeur.)

VIII-A-6-a - Héritage et attributs

Une sous-classe de SourceDeLumiere pourrait définir une puissance lumineuse industrielle qui fournit cent fois plus de lumière :

package SuperBougie
{
use Moose;

extends 'SourceDeLumiere';

has '+puissance_en_bougies', default => 100;
}

Le mot-clé extends est suivi par une liste de noms de classe à utiliser comme parents de la classe courante. Si c'était la seule ligne dans cette classe, les objets SuperBougie se comporteraient de la même manière que les objets SourceDeLumiere. Une instance SuperBougie aurait les attributs puissance_en_bougies et active ainsi que les méthodes allumer() et eteindre().

Le + au début du nom d'un attribut (comme puissance_en_bougies) indique que la classe courante fait quelque chose de spécial avec cet attribut. Ici, la super bougie surcharge (redéfinit) la valeur par défaut de la source de lumière, donc toute nouvelle SuperBougie créée donne autant de lumière que cent bougies habituelles.

Lorsque vous appelez allumer() ou eteindre() sur un objet SuperBougie, Perl cherchera la méthode dans la classe SuperBougie. S'il n'y a aucune méthode de ce nom dans la classe enfant, Perl regardera dans chaque classe parente, de manière récursive. Dans ce cas-ci, ces méthodes se trouvent dans la classe de SourceDeLumiere.

L'héritage des attributs fonctionne de façon similaire (voir perldoc Class::MOP).

VIII-A-6-b - Ordre de recherche des méthodes

Un appel de méthode implique toujours une stratégie de recherche. Cette stratégie établit comment Perl choisit la méthode appropriée. Cela peut sembler évident, compte tenu de la simplicité de la classe Chat, mais une grande partie de la puissance de l'OO vient de la recherchedes méthodes.

L'ordre de recherche des méthodes (ou l'ordre de résolution des méthodes ou MRO) est évident pour les classes ayant une seule parente. Regardez dans la classe de l'objet, puis dans sa parente et ainsi de suite jusqu'à ce que vous trouviez la méthode ou épuisiez les classes parentes. Les classes qui héritent de plusieurs parentes (héritage multiple)  Aeroglisseur étend tant Bateau que Voiture  nécessitent une recherche plus délicate. Le raisonnement sur l'héritage multiple est complexe. Évitez l'héritage multiple lorsque cela est possible.

Perl utilise une stratégie de résolution de méthode en profondeur. Il recherche la classe de la première parente nommée et récursivement dans toutes les parentes de cette parente avant de rechercher dans les autres classes parentes de la classe courante. Le pragma mro (Pragmas) fournit des stratégies de rechange comme la stratégie C3 MRO qui recherche dans les parentes immédiates d'une classe donnée avant de chercher dans l'une de leurs parentes.

Voir perldoc mro pour plus de détails.

VIII-A-6-c - Héritage et méthodes

Comme pour les attributs, les sous-classes peuvent redéfinir des méthodes. Imaginez une lumière que vous ne pouvez pas éteindre :

package BatonLumineux
{
use Moose;

extends 'SourceDeLumiere';

sub eteindre {}
}

L'appel de eteindre() sur un bâton lumineux ne fait rien, même si la même méthode de SourceDeLumiere fait quelque chose. La recherche de méthode trouvera la méthode de la sous-classe. Vous n'avez peut-être pas voulu faire cela. Quand vous le faites, utilisez le mot-clé override de Moose pour exprimer clairement votre intention.

À l'intérieur d'une méthode redéfinie, super() de Moose vous permet d'appeler la méthode de la classe parente que vous êtes en train de surcharger :

Package SourceDeLumiere::Grincheux
{
use Carp 'carp';
use Moose;

extends 'SourceDeLumiere';

override allumer => sub
{
my $self = shift;

carp "Impossible d'allumer une source de lumière allumée !"
if $self->active;

super();
};

override eteindre => sub
{
my $self = shift;

carp "Impossible d'éteindre une source de lumière éteinte!"
unless $self->active;

super();
};
}

Cette sous-classe ajoute un avertissement lorsque vous essayez d'allumer ou d'éteindre une source de lumière se trouvant déjà dans l'état demandé. La fonction super() recherche ensuite la mise en œuvre dans la parente la plus proche de la méthode courante, par ordre normal de résolution de méthode Perl. (Voir perldoc Moose::Manual::MethodModifiers pour plus d'options de recherche.)

VIII-A-6-d - Héritage et isa()

La méthode isa() de Perl retourne vrai si son invoquant est ou étend une classe nommée. Cet invoquant peut être le nom d'une classe ou une instance d'un objet :

say 'On dirait une SourceDeLumiere'
if $bougeoir->isa( 'SourceDeLumiere' );

say 'Les Hominidés ne s'allument pas'
unless $chimpanze->isa( 'SourceDeLumiere' );

VIII-A-7 - Moose et Perl OO

Moose offre de nombreuses fonctionnalités au-delà de l'OO par défaut de Perl. Certes, vous pouvez construire vous-même tout ce que vous obtenez avec Moose (Références liées), ou le bricoler avec une série de distributions du CPAN, mais Moose en vaut vraiment la peine. C'est un ensemble cohérent, avec une bonne documentation. De nombreux projets importants l'utilisent avec succès. Sa communauté de développement est mature et attentive.

Moose s'occupe des constructeurs, des destructeurs, des accesseurs et de l'encapsulation. Vous devez faire le travail de déclarer ce que vous voulez, mais ce que vous obtenez en retour est sûr et facile à utiliser. Les objets Moose peuvent étendre et utiliser des objets du système objet standard de Perl.

Moose permet également la métaprogrammation  manipuler vos objets par le biais de Moose même. Si vous vous êtes déjà demandé quelles méthodes sont disponibles sur une classe ou un objet ou quels attributs un objet prend en charge, cette information est disponible :

my $metaclass = Singe::Pantalons->meta;

say 'Les instances de Singe::Pantalons ont les attributs :';

say $_->nom for $metaclass->get_all_attributes;

say 'Les instances de Singe::Pantalons supportent les méthodes :';

say $_->fully_qualified_name
for $metaclass->get_all_methods;

Vous pouvez même voir quelles classes étendent une classe donnée :

my $metaclass = Singe->meta;

say ' Singe est la superclasse de :';

say $_ for $metaclass->subclasses;

Voir perldoc Class::MOP::Class pour plus d'informations sur les opérations de métaclasse et perldoc Class::MOP pour des informations de métaprogrammation Moose.

Moose et son protocole métaobjet (ou MOP) offrent la possibilité d'une meilleure syntaxe pour déclarer et travailler avec des classes et des objets en Perl. Ce code est valide :

use MooseX::Declare;

role EtreVivant { requires qw( name age diet ) }

role CalculerAge::De::AnneeNaissance { has 'annee_naiss',
is      => 'ro',
isa     => 'Int',
default => sub { (localtime)[5] + 1900 };

method age
{
return (localtime)[5] + 1900
- $self->annee_naiss;
}
}

class Chat with EtreVivant
with CalculerAge::De::AnneeNaissance
{
has 'nom',   is => 'ro', isa => 'Str';
has 'diete', is => 'rw';
}

Le module CPAN MooseX::Declare utilise Devel::Declare pour ajouter la nouvelle syntaxe Moose spécifique. Les mots-clés class, role et method réduisent la quantité de code standard nécessaire pour écrire du bon code orienté objet en Perl. Remarquez en particulier la nature déclarative de cet exemple, ainsi que l'absence de my $self = shift; dans age().

Si vous utilisez Perl 5.14 ou une version plus récente, Devel::Declare est moins utile ; Perl prend lui-même en charge un système d'ajout de mots-clés à la syntaxe. Dans ce cas, un module d'adaptation de la syntaxe comme MooseX::Méthode::Signatures ou Moops peut être plus à votre goût.

Alors que Moose ne fait pas partie du standard Perl, sa popularité garantit qu'il est disponible sur de nombreuses distributions de systèmes d'exploitation. Les distributions Perl telles que Strawberry Perl et ActivePerl (deux distributions de Perl pour Windows) l'incluent aussi. Même si Moose est un module du CPAN et pas une bibliothèque standard, sa propreté et sa simplicité le rendent essentiel à la programmation Perl moderne.

Une version light de Moose

Moose n'est pas une petite bibliothèque, mais il est puissant. Une autre solution populaire est Moo, une bibliothèque plus légère qui est presque entièrement compatible avec Moose. Moo n'a pas certaines des fonctionnalités de métaprogrammation de Moose, mais la majorité du code n'en a pas besoin. Vous pouvez facilement commencer un projet avec Moo et migrer sans aucune difficulté à Moose si et quand vous avez besoin de puissance supplémentaire.

VIII-B - Références liées

Le système objet standard Perl est volontairement minimal. Il n'a que trois règles :

- une classe est un paquetage ;

- une méthode est une fonction ;

- une référence (liée à une classe) est un objet.

Vous pouvez construire tout le reste sur ces trois règles. Ce minimalisme peut être peu pratique pour les grands projets  en particulier, les possibilités pour une plus grande abstraction par métaprogrammation (Génération de code) sont maladroites et limitées. Moose (Moose) est un meilleur choix pour les programmes modernes de plus de quelques centaines de lignes, bien que beaucoup de code existant utilise toujours l'OO par défaut de Perl.

Vous avez vu déjà les deux premières règles. La commande interne bless (le sens commun du verbe to bless est « bénir ») associe le nom d'une classe à une référence. Cette référence est maintenant un invoquant valide, et Perl effectuera sur elle la recherche de méthodes, en utilisant la classe associée.

Un constructeur est une méthode qui crée et « bénit » une référence, c'est-à-dire la lie à une classe. Par convention, les constructeurs ont généralement le nom new(), mais ce n'est pas une exigence. Les constructeurs sont également presque toujours des méthodes de classe.

bless prend deux opérandes, une référence et un nom de classe, et évalue la référence. La référence peut être toute référence valide, vide ou non. Il n'est pas nécessaire que la classe existe déjà. Vous pouvez même utiliser bless à l'extérieur d'un constructeur ou d'une classe... mais vous violez l'encapsulation en exposant les détails de la construction de l'objet en dehors d'un constructeur. Un constructeur peut être aussi simple que :

sub new
{
my $class = shift;
bless {}, $class;
}

Par conception, ce constructeur reçoit le nom de la classe comme invoquant de la méthode. Vous pouvez également coder en dur le nom d'une classe, au détriment de la flexibilité. Un constructeur paramétrable permet la réutilisation par héritage, délégation ou exportation.

Le type de référence utilisé est pertinent uniquement pour la façon dont l'objet stocke ses propres données d'instance. Il n'a pas d'autre effet sur l'objet résultant. Les références de hachage sont les plus communes, mais vous pouvez lier tout type de référence :

my $array_obj  = bless [],          $class;
my $scalar_obj = bless \$scalar,    $class;
my $func_obj   = bless \&some_func, $class;

Les classes Moose définissent des attributs d'objets de façon déclarative, mais l'OO par défaut de Perl est laxiste. Une classe représentant des joueurs de basket-ball qui stocke le numéro de maillot et la position pourrait utiliser un constructeur comme celui-ci :

package Joueur
{
sub new
{
my ($class, %attrs) = @_;
bless \%attrs, $class;
}
}

... et créer des joueurs comme ceci :

my $joel   = Joueur->new( numero => 10,
position => 'pivot' );

my $damien = Joueur->new( numero  => 0,
position => 'meneur' );

Les méthodes de la classe peuvent accéder directement aux attributs des objets en tant qu'éléments du hachage :

sub format
{
my $self = shift;
return '#'       . $self->{numero}
. ' joue '  . $self->{position};
}

... mais un programme utilisant la classe peut aussi le faire, donc tout changement de la représentation interne de l'objet peut nuire à un autre programme. Il est bien plus sûr d'utiliser des accesseurs :

sub numero   { return shift->{numero}   }
sub position { return shift->{position} }

... et maintenant vous commencez à écrire vous-même ce que Moose vous offre gratuitement. Mieux encore, Moose encourage les gens à utiliser des accesseurs au lieu de l'accès direct en cachant le code de génération d'accesseur. Adieu, la tentation.

VIII-B-1 - Recherche de méthode et héritage

Sur une référence liée, un appel de méthode de la forme :

my $numero = $joel->numero;

... recherche le nom de la classe associée à la référence liée $joel  dans ce cas, Joueur. Ensuite, Perl recherche dans Joueur une fonction (rappelez-vous que Perl ne fait aucune distinction entre fonctions dans un espace de noms et méthodes) nommée numero(). Si une telle fonction n'existe pas et que Joueur étend une classe parente, Perl regarde dans la classe parente (et ainsi de suite et ainsi de suite) jusqu'à ce qu'il trouve numero(). Si Perl trouve numero(), il appelle cette méthode avec $joel comme invoquant.

Garder propres les espaces de noms

Le module du CPAN namespace::autoclean peut aider à éviter les collisions accidentelles entre fonctions importées et méthodes.

Moose fournit le mot-clé extends pour suivre les relations d'héritage, mais le système objet de Perl utilise une variable globale de paquetage nommée @ISA. (10) Le mécanisme de résolution de méthode recherche dans chaque @ISA d'une classe pour trouver les noms de ses classes parentes. Si la classe JoueurAccidente hérite de la classe Joueur, vous pouvez écrire :

package JoueurAccidente
{
@JoueurAccidente::ISA = 'Joueur';
}

Le pragma parent (Pragmas) est plus propre (des programmes anciens peuvent aussi utiliser le pragma base, mais parent a remplacé base depuis Perl 5.10) :

package JoueurAccidente
{
use parent 'Joueur';
}

Moose a son propre métamodèle qui stocke des informations plus amples sur l'héritage. Celui-ci lui permet d'offrir des possibilités supplémentaires de métaprogrammation.

Vous pouvez hériter de plusieurs classes parentes :

package JoueurAccidente;
{
use parent qw( Joueur Hopital::Patient );
}

... bien que les mises en garde au sujet de l'héritage multiple et la complexité de la recherche de méthode s'appliquent. Envisagez à la place les rôles (Rôles) ou les modificateurs de méthode de Moose.

VIII-B-2 - AUTOLOAD

S'il n'y a aucune méthode utilisable dans la classe de linvoquant ou dans l'une de ses superclasses, Perl recherchera ensuite une fonction AUTOLOAD() (AUTOLOAD) dans chaque classe selon l'ordre de résolution de méthode choisi. Perl appellera la première fonction AUTOLOAD() qu'il trouve pour fournir ou refuser la méthode souhaitée.

AUTOLOAD() rend l'héritage multiple encore beaucoup plus difficile à comprendre.

VIII-B-3 - Surcharge de méthode et SUPER

Comme avec Moose, vous pouvez surcharger des méthodes dans l'OO standard de Perl. Contrairement à Moose, Perl ne prévoit aucun mécanisme explicite pour indiquer votre intention de surcharger la méthode d'une classe parente. Encore pire, n'importe quelle fonction que vous prédéclarez, déclarez, ou importez dans la classe fille peut surcharger une méthode ayant le même nom dans la classe parente. Même si vous oubliez d'utiliser le système override de Moose, au moins il a le mérite d'exister. L'OO basique de Perl n'offre pas une telle protection.

Pour surcharger une méthode dans une classe fille, déclarez simplement une méthode du même nom que la méthode dans la parente. Dans une méthode surchargée, vous pouvez appeler la méthode de la classe parente avec le préfixe SUPER:: :

sub overridden
{
my $self = shift;
warn 'Appel à overridden() dans la classe enfant !';
return $self->SUPER::overridden( @_ );
}

Le préfixe SUPER:: du nom de la méthode indique au mécanisme de recherche de méthode de rechercher une méthode redéfinie du nom approprié. Vous pouvez fournir vos propres arguments à la méthode surchargée, mais la plus grande partie du code réutilise @_. Pensez à bien récupérer l'invoquant si vous le faites.

Linconvénient de SUPER::

SUPER:: a un dysfonctionnement déroutant : il recherche dans la classe mère du paquetage dans lequel la méthode surchargée a été compilée. Si vous avez importé cette méthode depuis un autre paquetage, Perl recherchera volontiers dans la mauvaise classe mère. Le désir de rétrocompatibilité ascendante a gardé en place ce dysfonctionnement. Le module SUPER du CPAN offre une solution de contournement. super() de Moose ne souffre pas du même problème.

VIII-B-4 - Stratégies pour faire face aux références liées

Les références liées peuvent sembler minimales, difficiles et déroutantes. Elles le sont. Moose est beaucoup plus facile à utiliser, alors utilisez-le autant que possible. Si vous devez maintenir du code qui utilise des références liées, ou si vous ne pouvez pas encore convaincre votre équipe à utiliser Moose pleinement, vous pouvez contourner certains des problèmes de références liées avec de la discipline.

- ne mélangez pas fonctions et méthodes dans la même classe ;

- utilisez un fichier .pm unique pour chaque classe, sauf s'il s'agit d'une petite classe auxiliaire autonome utilisée depuis un seul endroit ;

- suivez les recommandations standard de Perl OO, en nommant les constructeurs new() et en utilisant $self comme nom de l'invoquant dans votre documentation ;

- utilisez des accesseurs le plus possible, même au sein des méthodes de votre classe. Un module comme Class::Accessor permet d'éviter le code répétitif ;

- évitez AUTOLOAD() autant que possible. Si vous devez l'utiliser, utilisez des déclarations de fonctions avancées (Déclarer des fonctions) pour éviter toute ambiguïté ;

- attendez-vous à ce que quelqu'un quelque part crée un jour une sous-classe (ou délègue, ou réimplémente l'interface) de vos classes. Facilitez-leur la tâche en ne supposant pas les détails sur le fonctionnement interne de votre code, en utilisant la forme à deux arguments de bless et en décomposant vos classes dans des unités plus petites responsables de code ;

- utilisez des modules auxiliaires comme Role::Tiny pour permettre une meilleure utilisation et réutilisation.

VIII-C - Réflexion

La réflexion (ou l'introspection) est le processus d'interrogation d'un programme au sujet de lui-même pendant l'exécution. En traitant le code sous forme de données, vous pouvez gérer le code de la même manière que les données. C'est le principe derrière la génération du code (Génération de code).

Class::MOP de Moose (Class::MOP) simplifie de nombreuses tâches de réflexion pour les systèmes d'objets. Si vous utilisez Moose, son système de métaprogrammation vous sera utile. Sinon, plusieurs autres idiomes Perl vous aideront à inspecter et manipuler les programmes en cours d'exécution.

VIII-C-1 - Vérifier si un module est chargé

Si vous connaissez le nom d'un module, vous pouvez vérifier si Perl estime qu'il a chargé ce module en regardant dans le hachage %INC. Lorsque Perl charge du code avec use ou require, il stocke dans %INC une entrée dont la clé est le chemin du fichier du module à charger et la valeur est le chemin complet sur le disque vers ce module. En d'autres termes, le chargement de Modern::Perl fait effectivement :

$INC{'Modern/Perl.pm'} =
'.../lib/site_perl/5.12.1/Modern/Perl.pm';

Les détails du chemin varieront en fonction de votre installation. Pour tester si Perl a chargé avec succès un module, convertir le nom du module dans la forme canonique de fichier et vérifier l'existence de cette clé dans %INC :

sub module_loaded
{
(my $nom_module = shift) =~ s!::!/!g;
return exists $INC{ $nom_module . '.pm' };
}

Comme dans le cas de @INC, tout code peut manipuler %INC n'importe où. Certains modules (tels que Test::MockObject ou Test::MockModule) manipulent %INC pour de bonnes raisons. Selon votre niveau de paranoïa, vous pouvez vérifier vous-même le chemin d'accès et le contenu attendu du paquetage.

La fonction is_class_loaded() du module Class::Load de CPAN le fait pour vous sans vous obliger à manipuler explicitement %INC.

VIII-C-2 - Vérifier si un paquetage existe

Pour vérifier qu'un paquetage existe quelque part dans votre programme  si du code quelque part a exécuté une directive package avec un nom, vérifiez si le paquetage hérite de UNIVERSAL. Tout ce qui étend UNIVERSAL doit d'une façon ou d'une autre fournir la méthode can(). Si ce paquetage n'existe pas, Perl lèvera une exception concernant un invoquant invalide ; donc englobez cet appel dans un bloc eval :

say "$pkg exists" if eval { $pkg->can( 'can' ) };

Une autre approche consiste à explorer les tables de symboles de Perl. Mais là, débrouillez-vous (si vous savez ce que vous faites).

VIII-C-3 - Vérifier si une classe existe

Comme Perl ne fait aucune distinction forte entre paquetages et classes, le mieux que vous puissiez faire sans Moose est de vérifier si un paquetage du nom de la classe attendue existe. Vous pouvez tester si le paquetage fournit new() en utilisant can(), mais rien ne garantit que le new() que vous trouverez peut-être soit une méthode ou un constructeur.

VIII-C-4 - Vérifier le numéro de version d'un module

Les modules n'ont pas à fournir des numéros de version, mais chaque paquetage hérite de la méthode VERSION() de la classe parente universelle UNIVERSAL (Le paquetage UNIVERSAL) :

my $version = $module->VERSION;

VERSION() retourne le numéro de version du module donné, s'il est défini. Sinon, elle retourne undef. Si le module n'existe pas, la méthode retournera également undef.

VIII-C-5 - Vérifier si une fonction existe

Pour vérifier si une fonction existe dans un paquetage, appelez can() comme une méthode de classe sur le nom du paquetage :

say "$func() exists" if $pkg->can( $func );

Perl lancera une exception si $pkg n'est pas un invoquant valide ; englobez l'appel de méthode dans un bloc eval si vous avez des doutes sur sa validité. Attention, une fonction mise en œuvre à l'aide de AUTOLOAD() (AUTOLOAD) peut fournir une réponse fausse si le paquetage de la fonction n'a pas prédéclaré la fonction ou surchargé can() correctement. C'est un bogue dans l'autre paquetage.

Utilisez cette technique pour déterminer si la méthode import() d'un module a importé une fonction dans l'espace de noms courant :

say "$func() importée!" if __PACKAGE__->can( $func );

Comme pour la vérification de l'existence d'un paquetage, vous pouvez faire vous-même le tour des tables de symboles, si vous avez la patience pour cela.

VIII-C-6 - Vérifier si une méthode existe

Il n'y a aucun moyen d'introspection infaillible pour distinguer une fonction d'une méthode.

VIII-C-7 - Rechercher dans les tables de symboles

Une table de symboles est un type spécial de hachage dont les clés sont les noms des symboles globaux du paquetage et les valeurs sont des typeglob. Une typeglob est une structure interne de données qui peut contenir une partie ou lentièreté d'un scalaire, un tableau, un hachage, un descripteur de fichier et une fonction.

Accédez à une table de symboles comme à un hachage en ajoutant de doubles deux-points au nom du paquetage. Par exemple, la table de symboles pour le paquetage SingeGrinder est disponible comme %SingeGrinder::.

Vous pouvez tester l'existence des noms de symboles spécifiques dans une table de symboles avec l'opérateur exists (ou manipuler la table de symboles pour ajouter ou supprimer des symboles, si vous le souhaitez). Sachez pourtant que certains changements dans les standards Perl ont modifié les détails de ce que les typeglobs stockent, et quand, et pourquoi.

Consultez la section « Symbol Tables » dans perldoc perlmod pour plus de détails, puis préférez les autres techniques dans cette section sur l'introspection. Si vous devez vraiment manipuler des tables de symboles et des typeglobs, pensez à utiliser plutôt le module Package::Stash du CPAN.

VIII-D - Perl OO avancé

La création et l'utilisation d'objets en Perl avec Moose sont faciles (Moose). La conception de bons programmes ne l'est pas. Vous devez trouver l'équilibre entre trop de conception et pas assez. Seule l'expérience pratique peut vous aider à comprendre les techniques de conception les plus importantes, mais plusieurs principes peuvent vous guider.

VIII-D-1 - Préférer la composition à l'héritage

Les débutants dans la conception OO abusent souvent de l'utilisation de l'héritage pour réutiliser le code et exploiter le polymorphisme. Le résultat est une hiérarchie de classes profonde avec dispersion des responsabilités dans les mauvais endroits. Maintenir ce code est difficile  qui sait où ajouter ou modifier un comportement ? Qu'advient-il lorsque le code dans un endroit se trouve en conflit avec le code déclaré ailleurs ?

L'héritage n'est qu'un des nombreux outils pour les programmeurs OO. Ce n'est pas toujours le bon outil ; c'est même souvent le mauvais outil. Une Voiture peut étendre Vehicule::AvecRoues (une relation est-une), mais Voiture peut mieux contenir plusieurs objets Roue comme attributs d'instance (une relation a-une).

La décomposition des classes complexes en entités plus petites, focalisées (des classes ou des rôles) améliore l'encapsulation et réduit la possibilité qu'une classe ou un rôle en fasse trop. Les entités plus petites, plus simples, et mieux encapsulées sont plus faciles à comprendre, tester et maintenir.

VIII-D-2 - Principe de responsabilité unique

Lorsque vous concevez votre système objet, examinez les responsabilités de chaque entité. Par exemple, un objet Employe peut représenter de l'information spécifique sur le nom d'une personne, ses coordonnées et autres données personnelles, alors qu'un objet Travail peut représenter ses responsabilités professionnelles. La séparation de ces entités par leurs responsabilités permet à la classe Employe de s'occuper uniquement du problème de la gestion de l'information spécifique sur qui est la personne et à la classe Travail de représenter ce que fait la personne. (Par exemple, deux Employe peuvent avoir un arrangement de partage de Travail, ou un Employe peut avoir le Travail de directeur financier et celui de directeur général.)

Lorsque chaque classe possède une responsabilité unique, vous améliorez l'encapsulation des données et des comportements spécifiques dans la classe et réduisez le couplage entre classes.

VIII-D-3 - Ne vous répétez pas

La complexité et la duplication compliquent le développement et la maintenance. Le principe DRY (Don't Repeat Yourself) doit vous inciter à rechercher et éliminer la duplication au sein du système. La duplication existe dans les données ainsi que dans le code. Au lieu de répéter les informations de configuration, les données de l'utilisateur et d'autres objets dans votre système, créez une seule représentation canonique de cette information à partir de laquelle vous pouvez générer les autres artefacts.

Ce principe permet de réduire la possibilité que des parties importantes de votre système se désynchronisent. Il vous aide également à trouver la représentation optimale du système et de ses données.

VIII-D-4 - Principe de substitution de Liskov

Le principe de substitution de Liskov suggère que vous devez être en mesure de remplacer une spécialisation d'une classe ou d'un rôle par l'original sans violer l'API de l'original. Autrement dit, un objet doit être aussi ou plus général en ce qui concerne ce qu'il attend et au moins aussi précis sur ce qu'il produit, que l'objet qu'il remplace.

Imaginez deux classes, une classe mère Dessert et sa classe fille TarteAuNoixDePecan. Si les classes suivent le principe de substitution Liskov, vous pouvez remplacer chaque utilisation des objets Dessert avec des objets TarteAuNoixDePecan dans la suite de tests, et tous les tests devraient réussir. Voir « IS-STRICTLY-EQUIVALENT-TO-A » de Reg Braithwaite pour plus de détails, http://weblog.raganwald.com/2008/04/is-strictly-equivalent-to.html.

VIII-D-5 - Sous-types et coercitions

Moose vous permet de déclarer et d'utiliser des types et les étend à travers des sous-types pour former des descriptions de plus en plus spécialisées de ce que représentent vos données et comment elles se comportent. Ces annotations de type aident à vérifier si les données sur lesquelles vous voulez travailler dans des fonctions ou des méthodes spécifiques sont appropriées et même à préciser les mécanismes de coercition des données d'un type vers un autre type.

Par exemple, vous pouvez permettre aux gens de fournir des dates sous forme de chaînes de caractères en entrée à un GrandLivre comptable tout en les représentant en interne comme des instances de type DateTime. Vous pouvez le faire en créant un type Date et en ajoutant une coercition sur le type chaîne. Voir Moose::Util::TypeConstraints et MooseX::Types pour plus d'informations.

VIII-D-6 - Immutabilité

Les débutants OO traitent souvent des objets comme s'ils étaient des suites d'enregistrements qui utilisent des méthodes pour obtenir et définir des valeurs internes. Cette technique simple conduit à la malheureuse tentation de répartir les responsabilités de l'objet dans l'ensemble du système.

Avec un objet bien conçu, vous lui dites quoi faire, pas comment le faire. En règle générale, si vous vous retrouvez accéder aux données de l'instance d'objet (même à l'aide d'accesseurs) à l'extérieur de l'objet lui-même, il se peut que vous ayez trop d'accès aux données internes d'un objet.

Vous pouvez éviter cet accès inapproprié en rendant vos objets immuables. Fournissez les données nécessaires à leurs constructeurs, puis interdisez toute modification de ces informations à partir de l'extérieur de la classe. N'exposez à aucune méthode de modification les données de l'instance. Une fois que vous avez construit un tel objet, vous savez qu'il est toujours dans un état valide. Vous ne pourrez jamais modifier ses données pour le mettre dans un état non valide.

Cela nécessite une discipline énorme, mais les systèmes qui en résultent sont robustes, testables et maintenables. Certains modèles de conception vont même jusqu'à interdire la modification des données d'instance au sein de la classe elle-même, mais cela est beaucoup plus difficile à réaliser.

IX - Style et efficacité

La qualité est importante. Les programmes ont des bogues. Les programmes ont besoin de maintenance et d'expansion. Les programmes ont plusieurs programmeurs.

Pour bien programmer, nous devons trouver l'équilibre entre finir le travail maintenant et rendre nos vies plus faciles à l'avenir, lors de la maintenance. Nous devons équilibrer temps, ressources et qualité. L'art de la programmation exige que nous le fassions au mieux de notre capacité.

Pour bien écrire en Perl, nous devons comprendre le langage. Nous devons aussi cultiver un sens du bon goût pour le langage et la conception des programmes. La seule façon de le faire est de pratiquer en maintenant du code et en lisant et écrivant du très bon code. Ce chemin n'a aucun raccourci, mais il a des repères.

IX-A - Écrire du Perl maintenable

La maintenabilité évalue de façon nébuleuse dans quelle mesure un programme est facile à comprendre et à modifier. Écrivez du code. Revenez-y dans six mois. Combien de temps vous faut-il pour corriger un bogue ou ajouter une fonctionnalité ? C'est cela la maintenabilité.

La maintenabilité ne prend pas en compte si vous devez rechercher la syntaxe d'une commande interne ou une fonction d'une bibliothèque. Elle ne mesure pas la façon dont quelqu'un qui n'a jamais programmé avant lira ou pas votre code. Il est plus intéressant de se demander si un programmeur compétent qui comprend le problème que vous essayez de résoudre trouvera facile ou difficile de modifier le programme. Quels sont les problèmes qui gênent la correction d'un bogue ou l'ajout correct d'une amélioration ?

Pour écrire un logiciel facile à maintenir, vous avez besoin d'expérience dans la résolution des problèmes réels, une compréhension des idiomes, des techniques et du style de votre langage de programmation et du bon goût. Vous pouvez développer tout cela en vous concentrant sur quelques principes :

- supprimez le code dupliqué. Les bogues se cachent dans les sections de code répété et similaire  lorsque vous corrigez un bogue dans un morceau de code, est-ce que vous devez le corriger aussi ailleurs ? Lorsque vous mettez à jour une section, devez-vous en mettre à jour d'autres ?
Les systèmes bien conçus ont peu de duplication. Ils utilisent des fonctions, des modules, des objets et des rôles pour extraire le code dupliqué dans des composants distincts qui modélisent avec précision le domaine du problème. Les meilleures conceptions vous permettent d'ajouter des fonctionnalités en supprimant du code ;

- nommez bien les entités. Votre code raconte une histoire. Chaque nom que vous choisissez pour une variable, une fonction, un module et une classe vous permet de clarifier ou d'obscurcir votre intention. Choisissez soigneusement vos noms. Si vous rencontrez des difficultés à choisir de bons noms, vous devrez peut-être repenser votre conception ou étudier votre problème plus en détail ;

- évitez l'ingéniosité inutile. Le code concis est bon lorsqu'il révèle ses intentions. Le code ingénieux cache votre intention derrière des astuces voyantes. Perl vous permet d'écrire le bon code au bon moment. Choisissez la solution la plus évidente lorsque cela est possible. L'expérience et le bon goût vous guideront.
Certains problèmes exigent des solutions astucieuses. Lorsque cela se produit, encapsulez ce code derrière une interface simple et documentez votre ingéniosité ;

- embrassez la simplicité. Si tout le reste est égal, un programme simple est plus facile à maintenir que sa version plus complexe. La simplicité signifie savoir ce qui est le plus important et faire exactement cela.
Parfois, vous avez besoin d'un code puissant, robuste. Parfois, vous avez besoin d'une seule ligne de script. La simplicité signifie faire la différence entre les deux cas de figure et utiliser seulement ce dont vous avez besoin. Il n'existe aucune excuse pour éviter la vérification des erreurs ou la modularité ou la validation ou la sécurité. Le code simple peut utiliser des fonctionnalités avancées. Le code simple peut utiliser des modules du CPAN et beaucoup d'entre eux. Le code simple peut nécessiter du travail pour être compris. Pourtant le code simple résout les problèmes de manière efficace, sans travail inutile.

IX-B - Écrire du Perl idiomatique

Perl s'inspire largement d'autres langages. Perl vous permet d'écrire le code que vous voulez écrire. Les programmeurs C écrivent souvent en Perl dans un style ressemblant au C, tout comme les programmeurs Java écrivent du Perl du style du Java. Les programmeurs Perl efficaces écrivent du Perl « perlien », embrassant les idiomes du langage.

- Comprenez la sagesse de la communauté. Les programmeurs Perl débattent souvent farouchement des techniques et des idiomes. Les programmeurs Perl partagent également souvent leur travail et pas seulement sur le CPAN. Faites attention et obtenez l'illumination sur les compromis entre les différents idéaux et styles.
Les développeurs du CPAN, les Mongueurs de Perl et les participants aux listes de diffusion ont de l'expérience durement acquise lors de la résolution des problèmes de multiples façons. Parlez-leur. Lisez leur code. Posez des questions. Apprenez d'eux et ils apprendront de vous.

- Suivez les normes de la communauté. Perl est une communauté de créateurs d'outils. Nous résolvons des problèmes divers, y compris l'analyse du code statique (Perl::Critic), le reformatage (Perl::Tidy) et les systèmes privés de distribution (CPAN::Mini, Carton, Pinto). Profitez de l'infrastructure du CPAN ; suivez le modèle du CPAN dans l'écriture, la documentation, le paquetage, les tests et la distribution de votre code.

- Lisez du code. Rejoignez des listes de diffusion telles que Perl Beginners (http://learn.perl.org/faq/beginners.html) ou, en français, perl@mongueurs.net, participez à des forums Perl (https://www.developpez.net/forums/f82/autres-langages/perl/), parcourez PerlMonks (http://perlmonks.org/), et immergez-vous dans la communauté en général. Voir http://www.perl.org/community.html. Lisez du code et essayez de répondre à des questions  même si vous ne postez jamais les réponses, c'est une excellente occasion d'apprendre.

IX-C - Écrire du Perl efficace

Écrire du code maintenable signifie concevoir du code maintenable. Une bonne conception vient des bonnes habitudes :

- écrivez du code testable. L'écriture d'une suite de tests efficaces demande les mêmes compétences en conception que l'écriture du code efficace. Un code est un code. De bons tests vous donnent aussi la confiance nécessaire pour modifier un programme tout en conservant son fonctionnement correct ;

- modularisez. Tracez des frontières en appliquant l'encapsulation et l'abstraction. Trouvez les bonnes interfaces entre composants. Nommez bien les choses et mettez-les au bon endroit. La modularité vous oblige à penser aux modèles et abstractions dans votre code pour comprendre comment ils semboîtent. Trouvez les parties qui ne correspondent pas bien. Améliorez-les jusqu'à ce qu'elles le fassent ;

- suivez des normes de codage sensées. Des directives efficaces discutent la gestion des erreurs, la sécurité, l'encapsulation, la conception des API, la mise en page du projet et d'autres facettes du code maintenable. D'excellentes directives aident les développeurs à communiquer entre eux et avec le code. Votre travail consiste à résoudre des problèmes avec du code. Laissez votre code parler clairement ;

- exploitez le CPAN. Les programmeurs Perl résolvent des problèmes. Ensuite, nous partageons ces solutions. Le CPAN est un démultiplicateur de force ; recherchez une solution ou une solution partielle à votre problème. Investissez du temps dans la recherche afin de trouver des solutions complètes ou partielles que vous pouvez réutiliser. Cela se révélera payant.
Si vous trouvez un bogue, signalez-le. Corrigez-le, si possible. Corrigez une faute de frappe. Demandez une nouvelle fonctionnalité. Dites « Merci ! » Nous sommes meilleurs ensemble que nous le sommes séparément. Nous sommes puissants et efficaces lorsque nous réutilisons du code.
Lorsque vous êtes prêt  lorsque vous créez quelque chose de nouveau ou corrigez quelque chose de vieux d'une manière réutilisable, partagez votre code. Rejoignez-nous. Nous résolvons des problèmes.

IX-D - Exceptions

Les bons programmeurs anticipent l'imprévu. Les fichiers qui devaient exister n'existeront pas. Un disque énorme qui ne serait jamais plein se remplira. Le réseau qui marche toujours tombe en panne. La base de données la plus fiable sera endommagée. Les exceptions se produisent. Un logiciel robuste doit les gérer. Si vous pouvez résoudre le problème, tant mieux ! Si vous ne le pouvez pas, collectez les informations pertinentes et réessayez.

Perl gère des circonstances exceptionnelles par des exceptions : un mécanisme dynamique de flot d'exécution, conçu pour détecter et traiter les erreurs.

IX-D-1 - Signalez les exceptions

Supposons que vous voulez écrire un fichier journal. Si vous ne pouvez pas ouvrir le fichier, quelque chose s'est mal passé. Utilisez die pour signaler l'exception... ou laissez autodie (Le pragma « autodie ») l'ajouter pour vous, dans ce cas :

sub open_log_file
{
my $nom = shift;
open my $fh, '>>', $nom
or die "Impossible d'ouvrir le fichier journal '$nom': $!";
return $fh;
}

die() définit la variable globale $@ à son opérande et quitte immédiatement la fonction actuelle sans rien retourner. Cette exception lancée remontera la pile des appels (Exécution contrôlée) jusqu'à ce que quelque chose l'intercepte. Si rien n'intercepte l'exception, le programme se terminera avec une erreur.

La gestion des exceptions utilise la même portée dynamique (Portée dynamique) que les symboles déclarés avec local.

IX-D-2 - Intercepter les exceptions

Parfois, il est utile d'autoriser une exception à mettre fin à l'exécution du programme. Un programme exécuté comme un processus chronométré pourrait lancer une exception lorsque les journaux d'erreurs sont remplis, provoquant un SMS en sortie pour les administrateurs. Pourtant, toutes les exceptions ne devraient pas être fatales. Votre programme pourrait avoir besoin de continuer à s'exécuter après certaines exceptions. D'autres exceptions pourraient seulement vous donner une chance de sauver le travail de votre utilisateur et s'arrêter proprement.

Utilisez la forme de bloc de l'opérateur eval pour intercepter une exception :

# le fichier journal ne peut pas s'ouvrir
my $fh = eval { open_log_file( 'monkeytown.log' ) };

Si l'ouverture du fichier réussit, $fh contiendra le descripteur de fichier. Si elle échoue, $fh restera indéfini, et le déroulement du programme se poursuivra.

Le bloc de eval introduit une nouvelle portée, à la fois lexicale et dynamique. Si open_log_file() a appelé d'autres fonctions et quelque chose signale finalement une exception, cet eval pourrait l'intercepter.

Un gestionnaire d'exceptions est un outil grossier. Il interceptera toutes les exceptions signalées dans sa portée dynamique. Pour vérifier quelle exception vous avez interceptée (ou si vous n'en avez intercepté aucune), vérifiez la valeur de $@. Soyez sûr de localiser $@ avant de tenter d'intercepter une exception, car $@ est une variable globale :

local $@;

# le fichier journal ne peut pas s'ouvrir
my $fh = eval { open_log_file( 'monkeytown.log' ) };

# exception interceptée
if (my $exception = $@) { ... }

Copiez immédiatement $@ dans une variable lexicale pour éviter la possibilité que du code subséquent écrase la variable globale $@. Vous ne savez jamais quoi d'autre a utilisé ailleurs un bloc eval et a réinitialisé $@.

$@ contient généralement une chaîne décrivant l'exception. Inspectez son contenu pour voir si vous pouvez gérer l'exception :

if (my $exception = $@)
{
die $exception
unless $exception =~ /^Can't open logging/;
$fh = log_to_syslog();
}

Relancez une exception en appelant à nouveau die(). Passez-lui l'exception existante ou une autre, si nécessaire.

Appliquer des expressions régulières aux exceptions de chaîne peut être fragile, parce que les messages d'erreur pourraient changer au fil du temps. Cela inclut les exceptions lancées par Perl même. Au lieu de lancer une exception sous forme de chaîne, vous pouvez utiliser une référence  même une référence liée  avec die. Cela vous permet de fournir beaucoup plus d'information dans votre exception : des numéros de lignes, des fichiers et d'autres informations de débogage. La récupération des informations à partir d'une structure de données est beaucoup plus facile que l'analyse des données d'une chaîne. Interceptez ces exceptions comme vous le feriez pour toute autre exception.

Le module Exception::Class du CPAN facilite la création et l'utilisation des objets exception :

package Zoo::Exceptions
{
use Exception::Class
'Zoo::AnimalEchappe',
'Zoo::HandlerEchappe';
}

sub cage_ouverte
{
my $self = shift;
Zoo::AnimalEchappe->throw
unless $self->contient_animal;
...
}

sub breche_ouverte
{
my $self = shift;
Zoo::HandlerEchappe->throw
unless $self->contient_handler;
...
}

IX-D-3 - Dangers des exceptions

Bien que lancer des exceptions soit relativement simple, les intercepter l'est moins. L'utilisation correcte de $@ vous oblige à naviguer parmi plusieurs risques subtils :

- son utilisation non localisée plus loin dans la portée dynamique peut modifier $@ ;

- $@ peut contenir un objet qui renvoie une valeur fausse dans le contexte booléen ;

- un gestionnaire de signal (en particulier le gestionnaire de signal DIE) peut modifier $@ ;

- la destruction d'un objet lors de la sortie de la portée peut appeler eval et modifier $@.

Perl 5.14 a remédié à certains de ces problèmes. Ils se produisent rarement, mais ils sont souvent difficiles à diagnostiquer et corriger. Le module Try::Tiny du CPAN améliore la sécurité de la gestion des exceptions et la syntaxe Try::Tiny a inspiré des améliorations à la manipulation des exceptions de Perl 5.14.

Try::Tiny est facile à utiliser :

use Try::Tiny;

my $fh = try   { open_log_file( 'monkeytown.log' ) }
catch { log_exception( $_ ) };

try remplace eval. Le bloc optionnel catch s'exécute uniquement lorsque try intercepte une exception. catch reçoit l'exception interceptée dans la variable sujet $_.

IX-D-4 - Exceptions internes

Perl signale lui-même plusieurs conditions exceptionnelles. perldoc perldiag énumère plusieurs « erreurs fatales récupérables ». Certaines sont des erreurs de syntaxe que Perl produit lors de l'échec de la compilation, mais vous pouvez intercepter les autres pendant l'exécution. Les plus intéressantes sont :

- utiliser une clé rejetée dans un hachage verrouillé (Verrouiller des tables de hachage) ;

- lier une non-référence avec bless (Références liées) ;

- appeler une méthode sur un invoquant invalide (Moose) ;

- impossibilité de trouver sur l'invoquant une méthode ayant le nom donné ;

- utilisation d'une valeur en entrée de façon non sécurisée (Données d'origine non fiable et le mode taint) ;

- modification d'une valeur en lecture seule ;

- exécution d'une opération non valide sur une référence (Références).

Bien sûr, vous pouvez aussi intercepter des exceptions produites par autodie (Le pragma « autodie ») et tout avertissement lexical promu au rang d'exception (Enregistrer vos propres avertissements).

IX-E - Pragmas

La majorité des modules Perl fournissent de nouvelles fonctions ou définissent des classes (Moose). D'autres, tels que strict ou warnings, influencent le comportement du langage même. Cette seconde classe de module s'appelle pragma ou module pragmatique. Par convention, les pragmas ont des noms en minuscules pour les différencier des autres modules.

IX-E-1 - Pragmas et portée

Les pragmas fonctionnent en exportant des comportements ou des informations spécifiques dans les portées lexicales de leurs appelants. Vous avez vu comment la déclaration d'une variable lexicale rend disponible un nom de symbole dans une portée. L'utilisation d'un pragma active également son comportement à l'intérieur d'une portée :

{
# $lexical pas visible; strict pas en vigueur
{
use strict;
my $lexical = 'disponible ici';
# $lexical est visible; strict est en vigueur
}
# $lexical à nouveau invisible; strict pas en vigueur
}

Tout comme les déclarations lexicales concernent les portées intérieures, les pragmas maintiennent leurs effets dans des portées intérieures :

# portée du fichier
use strict;

{
# portée intérieure, mais strict toujours en vigueur
my $interne = 'autre lexical';
}

IX-E-2 - Utiliser des pragmas

Utilisez un pragma comme vous le feriez pour tout autre module, à l'aide de l'instruction use. Les pragmas prennent des arguments, tels qu'un numéro minimal de version à utiliser ou une liste d'arguments pour modifier le comportement du pragma :

# nécessite des déclarations de variables, interdit les mots nus
use strict qw( subs vars );

# basé sur la sémantique du livre de 2012
use Modern::Perl '2012';

Parfois, vous devez désactiver la totalité ou une partie de ces effets dans une portée lexicale imbriquée. La commande interne no effectue un unimport (Importation), qui annule les effets d'un pragma. Par exemple, pour désactiver la protection de strict lorsque vous devez manipuler la table des symboles :

use Modern::Perl; # ou use strict;

{
no strict 'refs';
# manipulez la table des symboles ici
}

IX-E-3 - Pragmas utiles

Perl comprend plusieurs pragmas standard utiles.

- strict active la vérification des références symboliques par le compilateur, l'utilisation des mots nus et la déclaration des variables.

- warnings active les avertissements optionnels pour comportements obsolètes, involontaires et maladroits.

- utf8 indique au compilateur d'utiliser l'encodage UTF-8 pour comprendre le code source du fichier actuel.

- autodie active la vérification automatique des erreurs des appels système et des commandes internes.

- constant vous permet de créer des valeurs constantes à la compilation (mais regardez Const::Fast du CPAN pour une autre solution).

- vars vous permet de déclarer des variables globales de paquetage, comme $VERSION ou @ISA (Références liées).

- feature vous permet d'activer et désactiver individuellement les nouvelles fonctionnalités de Perl. L'instruction use 5.14; active toutes les fonctionnalités Perl 5.14 et le pragma strict, et use feature ':5.14'; fait la même chose. Ce pragma est plus utile pour désactiver les fonctionnalités individuelles dans une portée lexicale.

- less montre comment écrire un pragma.

Comme vous pouvez l'imaginer avec ce qui vient d'être dit sur le pragma less, vous pouvez écrire vos propres pragmas lexicaux en Perl pur. perldoc perlpragma explique comment le faire, alors que la documentation de $^H dans perldoc perlvar explique comment marche cette fonctionnalité.

Le CPAN a commencé à recueillir des pragmas non standard :

- autovivification désactive l'autovivification (Autovivification) ;

- indirect prévient l'utilisation de l'invocation indirecte (Objets indirects) ;

- autobox active le comportement de type objet pour les types de base de Perl (scalaires, références, tableaux et hachages) ;

- perl5i combine et active de nombreuses extensions expérimentales du langage dans un ensemble cohérent.

Ces outils ne sont pas encore largement utilisés, mais ils ont leurs champions. autovivification et indirect peuvent vous aider à écrire du code plus correct. autobox et perl5i sont des expérimentations sur ce que Perl pourrait devenir un jour ; cela vaut la peine de jouer avec eux dans de petits projets.

X - Gestion des programmes réels

Un livre peut vous apprendre à écrire de petits programmes pour résoudre des exemples de problèmes. Vous pouvez apprendre beaucoup de syntaxe de cette façon.

Pour écrire des programmes réels afin de résoudre des problèmes réels, vous devez apprendre à gérer le code écrit dans votre langage. Comment organiser le code ? Comment savoir s'il fonctionne ? Comment pouvez-vous le rendre robuste face à des anomalies ? Qu'est-ce qui rend le code concis, clair et maintenable ? Le Perl moderne vous permet de répondre à toutes ces questions.

X-A - Tests

Vous avez déjà testé votre code si vous l'avez déjà exécuté, remarqué quelque chose qui ne fonctionnait pas tout à fait comme il fallait, fait une modification, puis exécuté à nouveau. Le test est le processus de vérification que votre logiciel se comporte comme prévu. Les tests efficaces automatisent ce processus. Plutôt que de compter sur les humains pour effectuer parfaitement des vérifications manuelles répétées, laissez l'ordinateur le faire.

Perl fournit d'excellents outils pour vous aider à écrire les bons tests.

X-A-1 - Test::More

Les tests Perl commencent avec le module standard Test::More et sa fonction ok(). ok() prend deux paramètres, une valeur booléenne et une chaîne qui décrit l'objectif du test :

ok(   1, 'le nombre un devrait être vrai'               );
ok(   0, '... et le zéro ne le devrait pas'             );
ok(  '', 'la chaîne vide devrait être fausse'           );
ok( '!', '... et une chaîne non vide ne le devrait pas' );

done_testing();

Toute condition que vous pouvez tester dans votre programme peut finalement se résumer à une valeur binaire. Chaque assertion de test est une simple question avec une réponse par oui ou par non : est-ce que ce code se comporte comme je voulais ? Un programme complexe peut avoir des milliers de conditions individuelles. Aucun problème. Vous pouvez tester chacune de ces conditions, si vous souhaitez faire cet effort. Isoler des comportements particuliers dans des assertions individuelles vous aide à déboguer les erreurs de codage et de compréhension, surtout lorsque vous modifierez le code dans le futur.

La fonction done_testing() indique à Test::More que le programme a exécuté toutes les assertions demandées. Si le programme a rencontré une exception lors de l'exécution ou s'est arrêté de façon inattendue avant l'appel à done_testing(), le module de test vous informe que quelque chose s'est mal passé. Sans un mécanisme comme done_testing(), comment pourriez-vous le savoir ? Certes, cet exemple de code est trop simple pour échouer, mais le code qui est trop simple pour échouer échoue beaucoup plus souvent que vous ne le voulez.

Test::More vous permet d'établir un plan de test facultatif pour compter le nombre d'assertions individuelles que vous prévoyez d'exécuter :

use Test::More tests => 4;

ok(   1, 'le nombre un devrait être vrai'               );
ok(   0, '... et le zéro ne le devrait pas'             );
ok(  '', 'la chaîne vide devrait être fausse'           );
ok( '!', '... et une chaîne non vide ne le devrait pas' );

L'argument tests de Test::More définit le plan de test pour le programme. Il s'agit d'un filet de sécurité. Si moins de quatre tests ont été exécutés, quelque chose s'est mal passé. Si plus de quatre tests ont été exécutés, quelque chose s'est mal passé. done_testing() est plus facile, mais parfois un décompte exact peut être utile.

X-A-2 - Exécuter des tests

Cet exemple de fichier de test est un programme Perl complet qui produit la sortie :

ok 1 - le nombre un devrait être vrai
not ok 2 - ... et le zéro ne le devrait pas
#   Failed test '... et le zéro ne le devrait pas'
#   at truth_values.t line 4.
not ok 3 - la chaîne vide devrait être fausse
#   Failed test 'la chaîne vide devrait être fausse'
#   at truth_values.t line 5.
ok 4 - ... et une chaîne non vide ne le devrait pas
# Looks like you failed 2 tests of 4.

Cet affichage utilise un format de sortie de test appelé TAP, Test Anything Protocol (http://testanything.org/). Les tests TAP échoués produisent des messages de diagnostic comme une aide au débogage.

Le résultat affiché par ce petit exemple est facile à comprendre, mais cela peut devenir rapidement compliqué. Dans la plupart des cas, vous voulez savoir si tous les tests ont réussi ou le détail des échecs. Le module standard TAP::Harness interprète le TAP. Son programme prove qui lui est associé exécute des tests et affiche uniquement l'information la plus pertinente :

$ prove truth_values.t
truth_values.t .. 1/?
#   Failed test '... et le zéro ne le devrait pas'
#   at truth_values.t line 4.

#   Failed test 'la chaîne vide devrait être fausse'
#   at truth_values.t line 5.
# Looks like you failed 2 tests of 4.
truth_values.t .. Dubious, test returned 2
(wstat 512, 0x200)
Failed 2/4 subtests

Test Summary Report
-------------------
truth_values.t (Wstat: 512 Tests: 4 Failed: 2)
Failed tests:  2-3

Cela fait beaucoup d'information en sortie pour afficher ce qui est déjà évident : le deuxième et le troisième test échouent parce que le zéro et la chaîne vide ont la valeur fausse. Heureusement, il est facile à corriger par coercition booléenne ces tests qui échouent (Coercition booléenne) :

ok(   ! 0, '... et le zéro ne le devrait pas'   );
ok(  ! '', 'la chaîne vide devrait être fausse' );

Avec ces deux modifications, prove affiche maintenant :

$ prove truth_values.t
truth_values.t .. ok
All tests successful.

Voir perldoc prove pour d'autres options de test, telles que l'exécution de tests en parallèle (-j), l'ajout automatique de lib/ à la liste des répertoires où peuvent se trouver des modules Perl (-l), l'exécution récursive de tous les fichiers de test trouvés sous t/ (-r t) et l'exécution des tests lents en premier (--state=slow,save).

L'alias bash proveall suivant peut s'avérer utile :

alias proveall='prove -j9 --state=slow,save -lr t'

X-A-3 - Meilleures comparaisons

Même si le cœur de tous les tests automatisés est la condition booléenne « est-ce vrai ou faux ? », il est fastidieux de tout réduire à cette condition booléenne et le diagnostic pourrait être meilleur. Test::More offre plusieurs autres fonctions pratiques d'assertion.

La fonction is() compare deux valeurs en utilisant l'opérateur eq de Perl. Si les valeurs sont égales, le test réussit :

is(       4, 2 + 2, 'l\'addition devrait marcher' );
is( 'crêpe',   100, 'les crêpes sont numériques'  );

Comme vous vous en doutez sans doute, le premier test réussit et le second échoue avec un message de diagnostic :

t/is_tests.t .. 1/2
#   Failed test 'les crêpes sont numériques'
#   at t/is_tests.t line 8.
#          got: 'crêpe'
#     expected: '100'
# Looks like you failed 1 test of 2.

Là où ok() ne fournit que le numéro de ligne du test échoué, is() affiche les valeurs attendues et reçues.

is() applique un contexte scalaire implicite à ses valeurs (Prototypes). Cela signifie, par exemple, que vous pouvez vérifier le nombre d'éléments dans un tableau sans évaluer explicitement le tableau dans un contexte scalaire... et vous pouvez omettre les parenthèses :

my @cousins = qw( Rick Kristen Alex Kay Eric Corey );
is @cousins, 6, 'Je devrais avoir seulement six cousins';

... bien que certaines personnes préfèrent écrire scalar @cousins par souci de clarté.

La fonction inverse isnt() de Test::More compare deux valeurs en utilisant l'opérateur ne, et retourne la valeur vraie si elles ne sont pas égales. Elle aussi fournit un contexte scalaire à ses opérandes.

is() et isnt() appliquent toutes les deux des comparaisons de chaînes avec les opérateurs eq et ne. C'est presque toujours une bonne chose, mais utilisez la fonction cmp_ok()  qui vous oblige à fournir un opérateur de comparaison spécifique  pour les valeurs complexes telles que des objets avec surcharge (Surcharge) ou des variables dualvar (Dualvars) :

cmp_ok( 100, $solde_actuel, '<=',
'Je devrais avoir au moins 100 €' );

cmp_ok( $singe, $grand_singe, '==',
'La conversion en nombre des simiens devrait marcher' );

Les classes et les objets fournissent leurs propres façons intéressantes d'interagir avec les tests. Testez qu'une classe ou un objet hérite d'une autre classe (Héritage) avec isa_ok() :

my $singezilla = RobotSinge->new;
isa_ok( $singezilla, 'Robot' );
isa_ok( $singezilla, 'Singe' );

isa_ok() fournit son propre message de diagnostic en cas d'échec.

can_ok() vérifie si une classe ou un objet peuvent invoquer la méthode ou les méthodes demandées :

can_ok( $singezilla, 'manger_banane' );
can_ok( $singezilla, 'transformer', 'detruire_tokyo' );

La fonction is_deeply() compare deux références pour s'assurer que leur contenu est identique :

use Clone;

my $nombres   = [ 4, 8, 15, 16, 23, 42 ];
my $clonenbre = Clone::clone( $nombres );

is_deeply( $nombres, $clonenbre,
'clone() devrait produire des éléments identiques' );

Si la comparaison échoue, Test::More fera de son mieux pour fournir un diagnostic raisonnable indiquant la position de la première inégalité entre les structures. Voir les modules Test::Differences et Test::Deep du CPAN pour des tests plus configurables.

Test::More dispose de plusieurs fonctions de test, mais celles que nous venons de voir sont les plus utiles.

X-A-4 - Organiser des tests

Les distributions du CPAN doivent en principe inclure un répertoire t/contenant un ou plusieurs fichiers de test nommés avec l'extension .t. Lorsque vous construisez une distribution avec le module Module::Build ou ExtUtils::MakeMaker, l'étape de test exécute tous les fichiers t/*.t, résume leur sortie et réussit ou échoue sur l'ensemble des résultats de la suite de tests. Il n'y a aucune ligne directrice concrète sur la façon de gérer le contenu des fichiers .t individuels, mais deux stratégies sont populaires :

- chaque fichier .t doit correspondre à un fichier .pm ;

- chaque fichier .t doit correspondre à une fonctionnalité.

Une approche hybride est plus flexible ; un test peut vérifier si tous vos modules compilent, tandis que d'autres tests démontrent que chaque module se comporte comme prévu. Lorsque votre projet grandit, la seconde approche est plus facile à gérer. Gardez vos fichiers de test petits et ciblés et ils seront plus faciles à maintenir.

Des fichiers séparés de test peuvent également accélérer le développement. Si vous ajoutez à votre RobotSinge la capacité de souffler du feu, vous voudriez peut-être exécuter uniquement le fichier de test t/souffler_feu.t. Lorsque la fonctionnalité marche comme vous voulez, exécutez toute la série de tests pour vérifier si les changements locaux n'ont aucun effet global indésirable.

X-A-5 - Autres modules de test

Test::More s'appuie sur un mécanisme sous-jacent de test connu sous le nom de Test::Builder. Ce dernier module gère le plan de test et coordonne la sortie du test dans le TAP. Cette conception permet aux multiples modules de test de partager le même mécanisme Test::Builder. Par conséquent, le CPAN a des centaines de modules de test disponibles et ils peuvent travailler tous ensemble dans le même programme.

- Test::Fatal permet de vérifier si votre code signale (ou pas) des exceptions de façon appropriée. Vous pouvez également rencontrer Test::Exception.

- Test::MockObject et Test::MockModule permettent de tester des interfaces difficiles en les simulant (émulation produisant des résultats différents).

- Test::WWW::Mechanize permet de tester des applications Web, tandis que Plack::Test, Plack::Test::Agent et la sous-classe Test::WWW::Mechanize::PSGI peuvent le faire sans utiliser un serveur Web externe.

- Test::Database fournit des fonctions pour tester l'utilisation et les dysfonctionnements des bases de données. DBICx::TestDatabase aide à créer des schémas temporaires de bases de données de test construits avec DBIx::Class.

- Test::Class offre un mécanisme alternatif pour organiser des suites de tests. Il vous permet de créer des classes dans lesquelles des méthodes spécifiques groupent des tests. Vous pouvez hériter des classes de test tout comme les classes de votre code héritent les unes des autres. C'est un excellent moyen de réduire la duplication dans les suites de tests. Voir l'excellente série Test::Class de Curtis « Ovid » Poe http://www.modernperlbooks.com/mt/2009/03/organizing-test-suites-with-testclass.html. La distribution récente Test::Routine offre des possibilités similaires à travers l'utilisation de Moose (Moose).

- Test::Differences teste l'égalité des chaînes et structures de données et affiche toute différence dans ses diagnostics. Test::LongString ajoute des assertions similaires.

- Test::Deep teste l'équivalence des structures de données imbriquées (Structures de données imbriquées).

- Devel::Cover analyse l'exécution de votre suite de tests et fait un rapport sur la quantité de votre code testé effectivement. En général, plus c'est couvert, mieux c'est  bien que 100 % ne soit pas toujours possible, 95 % c'est beaucoup mieux que 80 %.

- Test::Most regroupe plusieurs modules utiles de test en un seul module parent. Il permet d'économiser temps et effort.

Voir le projet Perl QA (http://qa.perl.org/) pour plus d'informations sur les tests en Perl.

X-B - Gérer les avertissements

Bien qu'il y ait plusieurs façons d'écrire un programme Perl qui marche, certaines de ces façons peuvent être déroutantes, pas claires, et même incorrectes. Le système optionnel d'avertissements de Perl peut vous aider à éviter ces situations.

X-B-1 - Produire des avertissements

Utilisez la commande interne warn pour émettre un avertissement :

warn 'Quelque chose s\'est mal passé !';

warn imprime une liste de valeurs dans le descripteur de fichier STDERR (Entrée et sortie). Perl concaténera le nom de fichier et le numéro de la ligne ayant appelé warn sauf si le dernier élément de la liste se termine par un saut de ligne.

Le module standard Carp étend les mécanismes d'alerte de Perl. Sa fonction carp() signale un avertissement dans la perspective du code appelant. Étant donnée une fonction comme :

use Carp 'carp';

sub seulement_deux_arguments
{
my ($lop, $rop) = @_;
carp( 'Trop d\'arguments fournis' ) if @_ > 2;
...
}

... l'avertissement d'arité (Arité) comprendra le nom et le numéro de ligne du code appelant, pas seulement_deux_arguments(). La méthode cluck() de Carp est similaire, mais elle produit une trace de tous les appels de fonction jusqu'à la fonction en cours.

Le mode verbeux de Carp ajoute les traces de tous les avertissements produits par carp() et croak() (Rapports d'erreurs) à travers l'ensemble du programme :

$ perl -MCarp=verbose my_prog.pl

Utilisez Carp au lieu de warn ou die lors de l'écriture des modules (Modules).

X-B-2 - Activer et désactiver des avertissements

Parfois, des programmes plus anciens utilisent l'argument de ligne de commande -w, qui active les avertissements tout au long du programme, même dans les modules externes écrits et maintenus par d'autres personnes. C'est tout ou rien  mais cela peut vous être utile si vous avez le temps et l'énergie d'éliminer les avertissements réels et potentiels tout au long du code source.

L'approche moderne consiste à utiliser le pragma warnings ou un équivalent, comme use Modern::Perl;. Cela active les avertissements dans les portées lexicales. Si vous avez utilisé warnings dans une portée, vous indiquez que le code ne devrait produire normalement aucun avertissement.

Options d'avertissements globaux

L'option -W active les avertissements tout au long du programme de façon unilatérale, indépendamment de toute utilisation de warnings. L'option -X désactive les avertissements tout au long du programme de façon unilatérale. Aucun des deux n'est courant.

-w, -W et -X affectent tous la valeur de la variable globale $^W. Le code écrit avant l'introduction du pragma warnings avec Perl 5.6.0  au printemps 2000, pour que vous sachiez que c'est vieux  peut localiser $^W pour supprimer certains avertissements à l'intérieur d'une portée donnée.

X-B-3 - Désactiver des catégories d'avertissements

Pour désactiver certains avertissements particuliers à l'intérieur d'une portée, utilisez no warnings; avec une liste d'arguments. L'omission de la liste d'arguments désactive tous les avertissements à l'intérieur de cette portée.

perldoc perllexwarn répertorie toutes les catégories d'avertissements comprises par votre version de Perl. La plupart d'entre eux représentent des conditions vraiment intéressantes, mais certains peuvent être inutiles dans vos circonstances particulières. Par exemple, l'avertissement recursion se produira si Perl détecte qu'une fonction s'est appelée elle-même plus de cent fois. Si vous êtes confiant dans votre capacité à écrire des conditions d'arrêt de la récursion, vous pouvez désactiver cet avertissement dans la portée de la récursion (bien que les appels terminaux puissent être préférables ; Appels de fonctions et récursion terminale).

Si vous générez du code (Génération de code) ou redéfinissez des symboles localement, vous pouvez souhaiter désactiver les avertissements redefine.

Certains développeurs Perl expérimentés désactivent les avertissements de type Use of uninitialized value dans le code qui traite des chaînes et qui concatène des valeurs provenant de plusieurs sources. Si vous êtes prudent sur l'initialisation de vos variables, vous pouvez ne jamais avoir besoin de désactiver cet avertissement, mais parfois l'avertissement gêne l'écriture du code concis dans votre style local.

X-B-4 - Rendre les avertissements fatals

Si votre projet considère les avertissements aussi onéreux que les erreurs, vous pouvez les rendre fatals. Afin de transformer tous les avertissements en exceptions au sein d'une portée lexicale :

use warnings FATAL => 'all';

Vous pouvez également rendre fatales des catégories spécifiques d'avertissements, telles que l'utilisation de constructions obsolètes :

use warnings FATAL => 'deprecated';

Avec une bonne discipline, cela peut produire un code très robuste  mais soyez prudent. De nombreux avertissements proviennent des conditions d'exécution. Si votre suite de tests ne parvient pas à identifier tous les avertissements que vous pourriez rencontrer, votre programme peut s'arrêter à cause des exceptions d'exécution non interceptées. Gardez à l'esprit que les versions les plus récentes de Perl peuvent ajouter de nouveaux avertissements et les avertissements customisés seront également fatals (Enregistrer vos propres avertissements). Si vous activez les avertissements fatals, faites-le uniquement dans le code que vous contrôlez et jamais dans le code d'une bibliothèque censée être utilisée par d'autres personnes.

X-B-5 - Intercepter des avertissements

Si vous êtes prêt à y travailler, vous pouvez intercepter des avertissements comme vous le feriez pour des exceptions. La variable %SIG (voir perldoc perlvar) contient des gestionnaires pour les signaux hors bande lancés par Perl ou par votre système d'exploitation. Affectez à $SIG{__WARN__} une référence de fonction pour intercepter un avertissement :

{
my $warning;
local $SIG{__WARN__} = sub { $warning .= shift };

# faites quelque chose de risqué
...

say "Intercepté l'avertissement :\n$warning" if $warning;
}

Dans le gestionnaire d'avertissements, le premier argument est le message de l'avertissement. Certes, cette technique est moins utile que la désactivation lexicale des avertissements, mais elle peut convenir à la bonne utilisation de modules de test tels que Test::Warnings du CPAN, où le texte de l'avertissement est important.

Faites attention, car %SIG est globale. localisez-la dans la portée la plus petite possible, mais comprenez qu'elle reste toujours une variable globale.

X-B-6 - Enregistrer vos propres avertissements

Le pragma warnings::register vous permet de créer vos propres avertissements afin que les utilisateurs de votre code puissent les activer et désactiver dans les portées lexicales. Dans un module, utilisez le pragma warnings::register ainsi :

package Singe::Effrayant;

use warnings::register;

Cela va créer une nouvelle catégorie d'avertissements du nom du paquetage Singe::Effrayant. Activez ces avertissements avec use warnings 'Singe::Effrayant' et désactivez-les avec no warnings 'Singe::Effrayant'.

Utilisez warnings::enabled() pour tester si la portée lexicale de l'appelant a activé une catégorie d'avertissements. Utilisez warnings::warnif() pour produire un avertissement uniquement si les avertissements sont en vigueur. Par exemple, pour produire un avertissement dans la catégorie deprecated :

package Singe::Effrayant;

use warnings::register;

sub import
{
warnings::warnif( 'deprecated',
'empty imports from ' . __PACKAGE__ .
' are now deprecated' )
unless @_;
}

Voir perldoc perllexwarn pour plus de détails.

X-C - Fichiers

La majorité des programmes doivent interagir d'une façon ou d'une autre avec le monde réel, souvent en lisant, en écrivant ou en manipulant d'une autre façon des fichiers. L'origine de Perl comme outil pour les administrateurs système a produit un langage bien adapté pour le traitement du texte.

X-C-1 - Entrée et sortie

Un descripteur de fichier représente l'état courant d'un canal spécifique d'entrée ou de sortie. Chaque programme Perl a trois descripteurs de fichiers standard disponibles, STDIN (l'entrée au programme), STDOUT (la sortie du programme), et STDERR (la sortie d'erreurs du programme). Par défaut, tout ce que vous affichez en sortie avec print ou say va dans STDOUT, tandis que les erreurs et les avertissements vont dans STDERR. Cette séparation de la sortie vous permet de rediriger la sortie utile et les erreurs vers deux endroits différents  un fichier de sortie et les journaux d'erreurs, par exemple.

Utilisez la commande interne open pour obtenir un descripteur de fichier. Pour ouvrir un fichier en lecture :

open my $fh, '<', 'nomFichier'
or die "Impossible de lire '$nomFichier': $!\n";

Le premier opérande est une variable lexicale qui contiendra le descripteur de fichier résultant. Le deuxième opérande est le mode d'ouverture du fichier, qui détermine le type de l'opération sur le descripteur de fichier (lecture, écriture, ou ajout à la fin, par exemple). L'opérande final est le nom du fichier. Si l'ouverture échoue, la clause die va lancer une exception, avec la raison de l'échec dans la variable magique $!.

Vous pouvez ouvrir des fichiers pour y écrire, ajouter à la fin, lire et écrire, et plus encore. Les modes d'ouverture de fichiers les plus importants sont les suivants :

Symbole

Explication

<

Ouverture en lecture.

>

Ouverture en écriture, effacement du contenu existant si le fichier existe ou création d'un nouveau fichier dans le cas contraire.

>>

Ouverture en écriture, avec ajout à la fin du contenu existant si le fichier existe ou création d'un nouveau fichier dans le cas contraire.

+<

Ouverture pour lecture et écriture.

-|

Ouverture d'un pipe vers un processus externe pour lecture.

|-

Ouverture d'un pipe vers un processus externe pour écriture.

Vous pouvez même créer des descripteurs de fichiers qui lisent ou écrivent dans de simples scalaires Perl, en utilisant n'importe quel mode de fichier existant :

open my $lire_fh,   '<', \$entree_truquee;
open my $ecrire_fh, '>', \$sortie_capturee;

faire_quelque_chose_de_genial( $lire_fh, $ecrire_fh );

perldoc perlopentut explique en détail les usages plus exotiques de open, y compris sa capacité de lancer et contrôler d'autres processus, ainsi que l'utilisation de sysopen pour contrôle plus fin sur l'entrée et la sortie. perldoc perlfaq5 inclut des exemples de code permettant de réaliser de nombreuses tâches courantes d'entrée/sortie.

Vous rappelez-vous autodie  ?

Considérez que tous les exemples de cette section ont activé use autodie; et peuvent donc gérer les erreurs en toute sécurité. Si vous choisissez de ne pas utiliser autodie, n'oubliez pas de vérifier les valeurs de retour de tous les appels système pour gérer les erreurs de manière appropriée.

X-C-1-a - Unicode, couches E/S et modes de fichier

En plus du mode d'ouverture de fichier, vous pouvez ajouter une couche d'encodage E/S qui permet à Perl d'encoder ou décoder à partir d'un codage Unicode. Par exemple, si vous savez que vous allez lire un fichier écrit dans l'encodage UTF-8 :

open my $entree_fh,  '>:encoding(UTF-8)', $fichier_en_entree;

... ou écrire dans un fichier en utilisant l'encodage UTF-8 :

open my $sortie_fh, '<:encoding(UTF-8)', $fichier_en_sortie;

X-C-1-b - Fonction open à deux arguments

Des programmes anciens utilisaient souvent la forme à deux arguments de open(), qui concatène dans le même chaîne le mode d'ouverture du fichier avec le nom du fichier à ouvrir :

open my $fh, "> $un_fichier"
or die "Impossible d'écrire dans '$un_fichier': $!\n";

Perl doit extraire le mode du fichier du nom de fichier. C'est un risque ; quand Perl doit deviner ce que vous voulez dire, il peut deviner incorrectement. Pire encore, si $un_fichier provenait d'une entrée d'utilisateur non fiable, vous avez un problème potentiel de sécurité, car tout caractère inattendu pourrait changer le comportement de votre programme.

La fonction open() à trois arguments est un remplacement plus sûr pour ce code.

Les nombreux noms de DATA
Le descripteur de fichier du paquetage spécial global DATA représente le fichier courant. Lorsque Perl termine la compilation du fichier, il laisse DATA ouvert et pointant vers la fin de l'unité de compilation si le fichier comporte une section __DATA__ ou __END__. Tout texte qui survient après ce symbole est disponible en lecture à partir de DATA. L'ensemble du fichier est disponible si vous utilisez seek pour revenir au début du descripteur de fichier. C'est utile pour les programmes courts, autonomes. Voir perldoc perldata pour plus de détails.

X-C-1-c - Lire des fichiers

Pour lire depuis un descripteur de fichier ouvert en entrée, utilisez la commande interne readline, écrite aussi <>. Un idiome commun lit une ligne à la fois dans une boucle while() :

open my $fh, '<', 'un_fichier';

while (<$fh>)
{
chomp;
say "Lire une ligne '$_'";
}

En contexte scalaire, readline lit une seule ligne du fichier et retourne cette ligne, ou undef si elle atteint la fin du fichier (eof()). Chaque itération dans cet exemple retourne la ligne suivante ou undef. Cette syntaxe while vérifie si la variable utilisée pour itération est définie, de sorte que seule la condition de fin de fichier arrête la boucle. Autrement dit, c'est un raccourci pour :

open my $fh, '<', 'un_fichier';

while (defined($_ = <$fh>))
{
chomp;
say "Lire une ligne '$_'";
last if eof $fh;
}

Pourquoi utiliser while et pas for  ?

for impose le contexte de liste à son opérande. Dans le cas de readline, Perl va lire lentièreté du fichier avant de traiter son contenu. while exécute les itérations et lit une ligne à la fois. Lorsque l'utilisation de la mémoire est une préoccupation (par exemple avec de gros fichiers), utilisez while.

Chaque ligne lue par readline inclut le ou les caractères qui marquent la fin d'une ligne. Dans la majorité des cas, c'est une séquence spécifique à la plate-forme, consistant en un saut de ligne (\n), un retour chariot (\r), ou une combinaison des deux (\r\n). Utilisez chomp pour l'enlever.

La façon la plus propre de lire un fichier ligne par ligne en Perl est la suivante :

open my $fh, '<', $nomFichier;

while (my $ligne = <$fh>)
{
chomp $ligne;
...
}

Par défaut, Perl accède aux fichiers en mode texte. Si vous lisez des données binaires  comme un fichier multimédia ou un fichier compressé, utilisez binmode avant d'effectuer toute opération E/S. Cela forcera Perl à traiter les données du fichier comme des donnée pures, sans les modifier en aucune façon. Les modifications comprennent la traduction du \n en la séquence de saut de ligne spécifique à la plate-forme. Même si les plates-formes de type Unix peuvent ne pas avoir toujours besoin de binmode, les programmes portables agissent prudemment (Unicode et chaînes de caractères).

X-C-1-d - Écrire dans des fichiers

Pour écrire dans un descripteur de fichier ouvert en sortie, utilisez print ou say :

open my $out_fh, '>', 'output_file.txt';

print $out_fh "Voici une ligne de text\n";
say   $out_fh "... et en voici une autre";

Notez l'absence de virgule entre le descripteur de fichier et l'opérande qui le suit.

Désambiguïsation de descripteur de fichier

Le livre De l'art de programmer en Perl de Damian Conway conseille de prendre l'habitude de mettre le descripteur de fichier entre des accolades. Cela est nécessaire pour lever l'ambiguïté de l'analyse d'un descripteur de fichier contenu dans une variable composée (tableau ou hachage, par exemple), et cela ne nuira pas dans les cas les plus simples.

Tant print que say prennent une liste d'opérandes. Perl utilise la variable magique globale $, comme séparateur entre les valeurs de la liste. Perl utilise également la valeur courante de $\ comme dernier argument à print or say. Ainsi, ces deux lignes de code produisent le même résultat :

my @princes = qw( Corwin Eric Random ... );

print @princes;
print join( $,, @princes ) . $\;

X-C-1-e - Fermer des fichiers

Lorsque vous avez fini de travailler avec un fichier, fermez explicitement son descripteur de fichier (avec close) ou arrangez-vous pour sortir de sa portée. Perl le fermera pour vous. L'avantage d'appeler close explicitement est que vous pouvez vérifier et remédier à des erreurs spécifiques, telles que le manque d'espace sur un périphérique de stockage ou une connexion réseau interrompue.

Comme d'habitude, autodie gère ces vérifications pour vous :

use autodie qw( open close );

open my $fh, '>', $fichier;

...

close $fh;

X-C-1-f - Variables spéciales de gestion des fichiers

Pour chaque ligne lue, Perl incrémente la valeur de la variable $., qui sert de compteur de lignes.

readline utilise le contenu actuel de $/ comme séquence de fin de ligne. La valeur par défaut de cette variable est la séquence de caractères de fin de ligne la plus appropriée pour les fichiers texte sur votre plate-forme courante. Le mot ligne est impropre, cependant. Vous pouvez définir $/ pour contenir n'importe quelle séquence de caractères... mais, malheureusement, pas une expression régulière. Peut-être à partir de Perl 5.22. C'est utile pour des données très structurées que vous souhaitez lire enregistrement par enregistrement.

Pour lire un enregistrement à la fois à partir d'un fichier avec des enregistrements séparés par deux lignes vides, définissez $/ à la valeur \n\n. chomp sur un enregistrement lu supprimera la séquence double-saut de ligne.

Perl met par défaut sa sortie dans un tampon et effectue des opérations d'entrée/sortie seulement lorsque la quantité de données en attente dépasse un seuil. Cela lui permet de grouper les opérations d'E/S coûteuses au lieu d'écrire chaque fois de très petites quantités de données. Pourtant, parfois vous voulez envoyer les données dès que vous les avez sans attendre le remplissage de la mémoire tampon, surtout si vous écrivez un filtre de lignes de commande relié à d'autres programmes ou un service réseau orienté ligne.

La variable $| contrôle la mise en tampon du descripteur de fichier de sortie courant. Lorsqu'elle est définie à une valeur différente de zéro, Perl videra la sortie après chaque écriture dans le descripteur de fichier. Lorsqu'elle est définie à la valeur zéro, Perl utilisera sa stratégie par défaut de mise en mémoire tampon.

Écriture immédiate automatique

Les fichiers possèdent une stratégie par défaut de mise en tampon intégrale. Lorsqu'il est connecté à un terminal actif  mais pas un autre programme  STDOUT utilise une stratégie de mise en tampon de ligne, par laquelle Perl videra STDOUT chaque fois qu'il rencontrera un saut de ligne dans la sortie.

Au lieu d'encombrer votre code avec une variable globale, utilisez la méthode autoflush() sur un descripteur de fichier lexical :

open my $fh, '>', 'pecan.log';
$fh->autoflush( 1 );

...

Depuis Perl 5.14, vous pouvez utiliser n'importe quelle méthode fournie par IO::File sur un descripteur de fichier. Vous n'avez pas besoin de charger IO::File explicitement. En Perl 5.12, vous devez charger vous-même IO::File.

Les méthodes input_line_number() et input_record_separator() de IO::File font le travail de $. et $/ sur les descripteurs de fichiers individuels. Consultez la documentation de IO::File, IO::Handle et IO::Seekable.

X-C-2 - Répertoires et chemins

Travailler avec des répertoires est analogue à travailler avec des fichiers, sauf que vous ne pouvez pas écrire dans des répertoires. Au lieu de cela, vous enregistrez, déplacez, renommez et supprimez des fichiers. Ouvrez un descripteur de répertoire avec la commande interne opendir :

opendir my $dirh, '/home/singedompteur/taches/';

La commande interne readdir lit un répertoire. Comme avec readline, vous pouvez parcourir le contenu des répertoires un par un ou vous pouvez les affecter à un tableau en une seule fois :

# itération
while (my $file = readdir $dirh)
{
...
}

# aplatissement en une liste, affectation à un tableau
my @files = readdir $autre_dirh;

Perl 5.12 a ajouté une fonctionnalité où readdir définit $_ dans une boucle while :

use 5.012;

opendir my $dirh, 'taches/cirque/';

while (readdir $dirh)
{
next if /^\./;
say "Trouvé une tâche $_!";
}

La curieuse expression régulière dans cet exemple permet d'ignorer ce qu'on appelle des fichiers cachés sur les systèmes de type Unix, où un point en début de leur nom les empêche par défaut d'apparaître dans l'affichage du contenu de répertoires. Elle permet d'ignorer également les deux fichiers spéciaux . et .., qui représentent respectivement le répertoire courant et le répertoire parent.

Les noms retournés par readdir sont relatifs au répertoire même. Autrement dit, si le répertoire tâches/ contient trois fichiers nommés manger, boire, et sois_singe, readdir retournera manger, boire et sois_singe et non tâches/manger, tâches/boire, et tâches/sois_singe. En revanche, un chemin absolu est un chemin d'accès complet à son système de fichiers.

Fermez un descripteur de répertoire en le laissant sortir de la portée ou avec la commande interne closedir.

X-C-2-a - Manipuler des chemins de fichier

Perl offre une vue de type Unix de votre système de fichiers et interprétera de façon appropriée les chemins de fichier du style Unix pour votre système d'exploitation et système de fichiers. Si vous utilisez Microsoft Windows, vous pouvez utiliser le chemin C:/Mes documents/Robots/Bender/ aussi bien que le chemin C:\Mes Documents\Robots\Caprica Six\.

Même si Perl utilise constamment la sémantique des fichiers Unix, la manipulation des fichiers sur des environnements variés est beaucoup plus facile avec un module. La famille de modules standard File::Spec vous permet de manipuler les chemins de fichier en toute sécurité et de façon portable. Il est un peu maladroit, mais bien documenté.

Le module Path::Class du CPAN a une interface plus agréable. Utilisez la fonction dir() pour créer un objet représentant un répertoire et la fonction file() pour créer un objet représentant un fichier :

use Path::Class;

my $repas = dir( 'taches', 'cuisiner' );
my $file  = file( 'taches', 'sante', 'robots.txt' );

Vous pouvez obtenir des objets de type Fichier à partir de répertoires et vice versa :

my $diner      = $repas->file( 'calzone_vegetarienne' );
my $robots_dir = $liste_robots->dir;

Vous pouvez même ouvrir des descripteurs de fichiers de répertoires et fichiers :

my $dir_fh    = $dir->open;
my $robots_fh = $liste_robots->open( 'r' )
or die "Ouverture échouée : $!";

Tant Path::Class::Dir que Path::Class::File offrent des comportements utiles  faites attention pourtant si vous utilisez un objet Path::Class quelconque avec un opérateur ou une fonction qui attend une chaîne contenant un chemin de fichier, car vous devez convertir vous-même l'objet en chaîne. C'est un inconvénient persistant, mais mineur. (Si vous trouvez cela pénible, essayez Path::Tiny comme solution de rechange.)

my $contenu = lire_dans_nomFichier( "$diner" );

X-C-3 - Manipuler des fichiers

En plus de lire et écrire des fichiers, vous pouvez également les manipuler comme vous le feriez directement depuis une ligne de commande ou d'un gestionnaire de fichiers. Les opérateurs de test de fichier, collectivement appelés opérateurs -X, examinent les attributs des fichiers et répertoires. Pour tester si un fichier existe :

say 'Present !' if -e $nomFichier;

L'opérateur -e a un seul opérande, le nom d'un fichier ou un descripteur de fichier ou de répertoire. Si le fichier existe, l'expression retournera une valeur vraie. perldoc -f -X répertorie tous les autres tests de fichiers ; les plus populaires sont :

- -f
qui retourne la valeur vraie si son opérande est un simple fichier ;

- -d
qui retourne la valeur vraie si son opérande est un répertoire ;

- -r
qui retourne la valeur vraie si les autorisations du fichier de son opérande permettent la lecture par l'utilisateur courant ;

- -s
qui renvoie la valeur vraie si son opérande est un fichier non vide.

Recherchez la documentation de ces opérateurs avec perldoc -f -r, par exemple.

La commande interne rename peut renommer un fichier ou le déplacer entre des répertoires. Elle prend deux opérandes, l'ancien et le nouveau chemin du fichier :

rename 'etoile_de_la_mort.txt', 'puits_de_carbone.txt';

# ou plus élégant :
rename 'etoile_de_la_mort' => 'puits_de_carbone .txt';

Il n'y a pas de commande interne pour copier un fichier, mais le module standard File::Copy fournit à la fois les fonctions copy() et move(). Utilisez la commande interne unlink pour supprimer un ou plusieurs fichiers. (La commande interne delete supprime un élément d'un hachage, pas un fichier du système de fichiers.) Ces fonctions et commandes internes retournent toutes la valeur vraie pour succès et initialisent $! en cas d'erreur.

Mieux que File::Spec

Path::Class fournit des méthodes pratiques pour vérifier certains attributs de fichier ainsi que pour supprimer complètement des fichiers de façon portable.

Perl suit son répertoire de travail courant. Par défaut, c'est le répertoire actif d'où vous avez lancé le programme. La fonction cwd() du module standard Cwd retourne le nom du répertoire de travail courant. La commande interne chdir tente de modifier le répertoire de travail courant. Pour travailler avec des fichiers ayant des chemins relatifs, il est essentiel de travailler à partir du répertoire correct.

Changer de répertoire

Si vous aimez les outils de ligne de commande pushd et popd, essayez File::pushd.

X-D - Modules

Beaucoup de gens considèrent le CPAN (Le CPAN) comme l'avantage principal de Perl. Le CPAN est un système pour trouver et installer des modules. Un module est un paquetage contenu dans son propre fichier et pouvant être chargé avec use ou require. Un module doit être du code Perl valide. Il doit se terminer par une expression qui retourne une valeur vraie de sorte que l'analyseur Perl sache qu'il a chargé et compilé le module avec succès. Il n'y a aucune autre exigence, juste des conventions fortes.

Lorsque vous chargez un module, Perl fractionne le nom du paquetage sur les doubles deux-points (::) et transforme les composants du nom de paquet dans un chemin de fichier. Cela signifie que use SingeEtrange; indique à Perl de rechercher un fichier nommé SingeEtrange.pm dans chaque répertoire du tableau @INC, dans l'ordre, jusqu'à ce qu'il en trouve un ou épuise la liste.

De même, use SingeEtrange::Persistance; demande à Perl de rechercher un fichier nommé Persistance.pm dans chaque répertoire nommé SingeEtrange/ présent dans chaque répertoire dans @INC, et ainsi de suite. use SingeEtrange::UI::Mobile; indique à Perl de rechercher un chemin relatif du fichier SingeEtrange/UI/Mobile.pm dans chaque répertoire dans @INC.

Le fichier résultant peut contenir ou pas une déclaration de paquetage correspondant à son nom de fichier  ce n'est pas une exigence technique, mais cela facilite la compréhension.

Astuces perldoc

perldoc -l Module::Name affichera le chemin complet vers le fichier .pm pertinent, si ce fichier contient de la documentation sous forme de POD. perldoc -lm Module::Name affichera le chemin complet vers le fichier .pm. perldoc -m Module::Name affichera le contenu du fichier .pm.

X-D-1 - Utiliser et importer

Lorsque vous chargez un module avec use, Perl le charge à partir du disque, puis appelle sa méthode import(), en passant les arguments que vous avez éventuellement fournis. Par convention, la méthode import() d'un module prend une liste des noms et exporte des fonctions et autres symboles dans l'espace de noms appelant. Ce n'est qu'une convention ; un module peut refuser de fournir une méthode import(), ou sa méthode import() peut avoir d'autres comportements. Des pragmas (Pragmas) tels que strict utilisent les arguments pour modifier le comportement de la portée lexicale appelante au lieu d'exporter des symboles :

use strict;
# ... appelle strict->import()

use CGI ':standard';
# ... appelle CGI->import( ':standard' )

use feature qw( say switch );
# ... appelle feature->import( qw( say switch ) )

La commande interne no appelle la méthode unimport() d'un module, si elle existe, en passant tous les arguments. Cela est plus fréquent avec les pragmas qui introduisent ou modifient un comportement par le biais de import() :

use strict;
# aucune référence symbolique, aucun mot nu
# exige la déclaration des variables

{
no strict 'refs';
# références symboliques permises
# les 'subs' et 'vars' de strict toujours en vigueur
}

Tant use que no prennent effet lors de la compilation, de sorte que :

use Module::Name qw( liste arguments );

... est identique à :

BEGIN
{
require 'Module/Name.pm';
Module::Name->import( qw( liste arguments ) );
}

De la même façon :

no Module::Name qw( liste arguments );

... est identique à :

BEGIN
{
require 'Module/Name.pm';
Module::Name->unimport(qw( liste d'arguments ));
}

... y compris require du module.

Les méthodes manquantes ne nous manquent jamais

Si les méthodes import() ou unimport() n'existent pas dans le module, Perl ne va pas générer un message d'erreur. Elles sont vraiment facultatives.

Vous pouvez appeler directement import() et unimport(), bien qu'il soit peu logique de le faire en dehors d'un bloc BEGIN ; une fois la compilation terminée, les effets de import() ou unimport() peuvent être limités.

Tant use que require respectent la casse (c'est-à-dire la distinction entre majuscules et minuscules). Bien que Perl connaisse la différence entre strict et Strict, votre combinaison de système d'exploitation et système de fichiers ne la connaît peut-être pas. Si vous écrivez use Strict;, Perl ne trouvera pas strict.pm sur un système de fichiers sensible à la casse. Avec un système de fichiers insensible à la casse, Perl chargera volontiers Strict.pm, mais rien ne se passera quand il essaie d'appeler Strict->import(). (strict.pm déclare un paquetage nommé strict.)

Les programmes portables font attention à la casse, même s'ils n'en ont pas besoin.

X-D-2 - Exporter

Un module peut rendre les symboles globaux de paquetage disponibles à d'autres paquetages à travers un processus connu sous le nom d'exportation  souvent en appelant import() implicitement ou directement.

Le module standard Exporter est le moyen habituel pour exporter les symboles d'un module. Exporter repose sur la présence des variables globales de paquetages  en particulier @EXPORT_OK et @EXPORT, qui contiennent une liste de symboles à exporter sur demande.

Examinons un module SingeEtrange::Utilites qui offre plusieurs fonctions autonomes utilisables dans tout le système :

package SingeEtrange::Utilites;

use Exporter 'import';

our @EXPORT_OK = qw( round translate screech );

...

Tout autre programme ou module peut maintenant utiliser ce module et éventuellement, importer une partie ou chacune des trois fonctions exportées. Vous pouvez également exporter des variables :

push @EXPORT_OK, qw( $aragnee $saki $ecureuil );

Exportez des symboles par défaut en les énumérant dans @EXPORT au lieu de @EXPORT_OK :

our @EXPORT = qw( singe_danse singe_dort );

... de sorte que toute instruction use SingeEtrange::Utilities; importera les deux fonctions. Sachez cependant que le fait de spécifier des symboles à importer conduira à la non-importation des symboles par défaut ; vous obtenez seulement ce que vous demandez. Pour charger un module sans importer les symboles, utilisez une liste vide explicite :

# rend le module disponible, mais n'importe rien
use StrangeMonkey::Utilities ();

Indépendamment de toute liste d'importations, vous pouvez toujours appeler des fonctions depuis un autre paquetage avec leurs noms complets :

SingeEtrange::Utilities::cri();

Exportation simplifiée

Le module Sub::Exporter du CPAN fournit une interface plus agréable pour exporter des fonctions sans utiliser des variables globales de paquetage. Il offre également des options plus puissantes. Toutefois, Exporter peut exporter des variables, tandis que Sub::Exporter exporte uniquement des fonctions.

X-D-3 - Organiser le code en modules

Perl ne vous oblige pas à utiliser des modules, paquetages ou espaces de noms. Vous pouvez mettre tout votre code dans un seul fichier .pl ou dans plusieurs fichiers .pl que vous appelez avec require selon vos besoins. Vous avez la possibilité de gérer votre code de la façon la plus appropriée, compte tenu de votre style de développement, du formalisme, du risque et de l'importance du projet, ainsi que de votre expérience et de votre facilité à déployer du code.

Cela dit, un projet ayant plus de quelques centaines de lignes de code gagne à être organisé en modules :

- les modules aident à imposer une séparation logique entre des entités distinctes du système ;

- les modules offrent une interface de séparation, procédurale ou OO ;

- les modules encouragent une organisation naturelle du code source ;

- l'écosystème de Perl possède de nombreux outils consacrés à la création, au maintien, à l'organisation et au déploiement des modules et des distributions ;

- les modules fournissent un mécanisme de réutilisation du code.

Même si vous n'utilisez pas une approche orientée objet, la modélisation de chaque entité ou responsabilité distincte dans votre système avec son propre module rassemble le code lié et sépare le code séparé.

X-E - Distributions

Une distribution est une collection de métadonnées et de modules (Modules) qui forme une seule unité redistribuable, testable et installable. La façon la plus simple de configurer, construire, rassembler dans un paquetage, tester et installer du code Perl est de suivre les conventions du CPAN. Ces conventions régissent la manière de créer le paquetage d'une distribution et de résoudre ses dépendances, où installer le logiciel, comment vérifier qu'il fonctionne, comment afficher la documentation et comment gérer un dépôt logiciel. Toutes ces directives sont issues du consensus de milliers de contributeurs travaillant sur des dizaines de milliers de projets. Une distribution construite selon les normes CPAN peut être testée sur plusieurs versions de Perl sur plusieurs plates-formes matérielles différentes dans les quelques heures suivant son ajout sur le CPAN, avec les erreurs signalées automatiquement aux auteurs  tout cela sans intervention humaine.

Vous pouvez choisir de ne jamais diffuser tout votre code sous forme de distributions publiques sur CPAN, mais vous pouvez utiliser les outils et conventions CPAN pour gérer même le code privé. La communauté Perl a construit cette étonnante infrastructure ; pourquoi ne pas en profiter ?

X-E-1 - Attributs d'une distribution

Outre un ou plusieurs modules, une distribution comprend plusieurs autres fichiers et répertoires :

- Build.PL ou Makefile.PL, un programme pilote permettant de configurer, construire, tester, regrouper et installer la distribution ;

- MANIFEST, une liste de tous les fichiers contenus dans la distribution. Cela permet aux outils de vérifier si la distribution est complète ;

- META.yml et/ou META.json, un fichier contenant des métadonnées à propos de la distribution et de ses dépendances ;

- README, une description de la distribution, son intention, et les informations sur son droit d'auteur et octroi de licences ;

- lib/, le répertoire contenant des modules Perl ;

- t/, un répertoire contenant des fichiers de test ;

- Changes, un fichier journal reprenant chaque changement significatif à la distribution.

Une distribution bien formée doit contenir un nom et un numéro de version unique (souvent obtenues de son module principal). Toute distribution que vous téléchargez à partir du réseau public CPAN doit se conformer à ces normes. Le service CPANTS (http://cpants.perl.org/) évalue si chaque distribution téléchargée respecte les lignes directrices et les conventions de paquetage et recommande des améliorations. Suivre les directives CPANTS ne signifie pas que le code fonctionne, mais que les outils de paquetage CPAN devraient comprendre la distribution.

X-E-2 - Outils CPAN pour gérer les distributions

Perl est livré en standard avec plusieurs outils pour gérer les distributions :

- CPAN.pm est le client officiel de CPAN. Bien que par défaut ce client installe des distributions du CPAN public, vous pouvez également l'utiliser sur votre propre dépôt logiciel à la place ou en plus du dépôt public ;

- Module::Build est une suite d'outils en pur Perl pour configurer, construire, installer et tester de distributions. Elle fonctionne avec les fichiers Build.PL ;

- ExtUtils::MakeMaker est un outil plus ancien, que Module::Build est destiné à remplacer. Il est encore largement utilisé, mais il est en mode maintenance et reçoit uniquement des corrections de bogues critiques. Il fonctionne avec les fichiers Makefile.PL ;

- Test::More (Tests) est le module fondamental et le plus largement utilisé pour écrire des tests automatisés pour les logiciels Perl ;

- TAP::Harness et prove (Exécuter des tests) exécutent des tests et interprètent et communiquent leurs résultats.

En outre, plusieurs modules non standard du CPAN vous facilitent la vie en tant que développeur :

- App::cpanminus est un client CPAN sans configuration. Il gère les cas les plus courants, utilise peu de mémoire, et travaille rapidement ;

- App::perlbrew vous aide à gérer les installations multiples de Perl. Installez des nouvelles versions de Perl pour tests ou production, ou pour isoler des applications et leurs dépendances ;

- CPAN::Mini et la commande cpanmini vous permettent de créer votre propre miroir (privé) du réseau public CPAN. Vous pouvez ajouter dans ce référentiel vos propres distributions et gérer les versions des modules publics disponibles dans votre organisation ;

- Dist::Zilla automatise à distance les tâches de distribution courantes. Comme il utilise soit Module::Build, soit ExtUtils::MakeMaker, il peut remplacer votre utilisation directe de ceux-ci. Voir http://dzil.org/ pour un tutoriel interactif ;

- Test::Reporter vous permet de générer le rapport des résultats des suites de tests automatisés pour les distributions que vous installez, fournissant à leurs auteurs plus de données sur les défaillances ;

- Carton et Pinto sont deux projets récents qui aident à gérer et installer les dépendances du code. Aucun n'est encore largement utilisé, mais ils sont tous deux en cours de développement actif.

X-E-3 - Conception des distributions

Le processus de conception d'une distribution pourrait remplir un livre (voir Writing Perl Modules for CPAN de Sam Tregar), mais quelques principes de conception vous aideront. Commencez avec un utilitaire tel que Module::Starter ou Dist::Zilla. Le coût initial de l'apprentissage de la configuration et des règles peut sembler un investissement exagéré, mais l'avantage d'avoir tout mis en place de la bonne façon (et dans le cas de Dist::Zilla, ne jamais devenir obsolète) vous dispensera d'un travail fastidieux.

Une distribution devrait suivre plusieurs lignes directrices n'ayant pas à voir avec le code :

- chaque distribution effectue un seul but bien défini. Ce but peut même inclure plusieurs distributions connexes en les regroupant dans un seul paquet installable. Décomposez votre logiciel en distributions individuelles pour gérer correctement leurs dépendances et respecter leur encapsulation ;

- chaque distribution contient un numéro unique de version. Les numéros de version doivent toujours augmenter. La politique de « version sémantique » (http://semver.org/) à trois nombres (version majeure, version mineure, version corrective) est saine et compatible avec l'approche de Perl ;

- chaque distribution fournit une API bien définie. Une suite complète de tests automatisés peut vérifier si vous maintenez cette API entre les versions différentes. Si vous utilisez un miroir CPAN local pour installer vos propres distributions, vous pouvez réutiliser l'infrastructure CPAN pour tester les distributions et leurs dépendances. Vous obtenez facilement l'intégration en testant les composants réutilisables ;

- les tests de distribution sont utiles et reproductibles. L'infrastructure CPAN met en œuvre des rapports de test automatisés. Utilisez-les ! ;

- les interfaces sont simples et efficaces. Évitez l'utilisation des symboles globaux et les exports par défaut ; permettez aux gens d'utiliser uniquement ce dont ils ont besoin. Ne polluez pas leurs espaces de noms.

X-F - Le paquetage UNIVERSAL

Le paquetage standard UNIVERSAL fourni par Perl est l'ancêtre de tous les autres paquetages, au sens orienté objet (Moose). UNIVERSAL fournit à ses classes filles quelques méthodes à utiliser, hériter ou redéfinir.

X-F-1 - La Méthode VERSION()

La méthode VERSION() retourne la valeur de la variable $VERSION du paquetage ou de la classe qui l'invoque. Si vous fournissez un numéro de version en tant que paramètre optionnel, la méthode lèvera une exception si la $VERSION interrogée n'est pas égale ou supérieure au paramètre.

Étant donné un module SingeHurleur de version 1.23 :

my $hm = SingeHurleur->new;

say SingeHurleur->VERSION;    # affiche 1.23
say $hm->VERSION;             # affiche 1.23
say $hm->VERSION( 0.0  );     # affiche 1.23
say $hm->VERSION( 1.23 );     # affiche 1.23
say $hm->VERSION( 2.0  );     # exception !

Il est très rarement utile de surcharger VERSION().

X-F-2 - La méthode DOES()

La méthode DOES() prend en charge l'utilisation des rôles (Rôles) dans les programmes. Passez-lui un invoquant et le nom d'un rôle, et la méthode retournera la valeur vraie si la classe appropriée remplit ce rôle d'une façon ou d'une autre, que ce soit par héritage, délégation, composition, application de rôle, ou tout autre mécanisme.

La mise en œuvre par défaut de DOES() revient à isa(), parce que l'héritage est un mécanisme par lequel une classe peut remplir un rôle. Étant donné un Capucin :

say Capucin->DOES( 'Singe'      );  # prints 1
say $cappy->DOES(  'Singe'      );  # prints 1
say Capucin->DOES( 'Invertebre' );  # prints 0

Surchargez DOES() si vous consommez manuellement un rôle ou fournissez d'une quelconque façon une équivalence allomorphique.

X-F-3 - La méthode can()

La méthode can() accepte une chaîne contenant le nom d'une méthode. Elle retourne une référence vers la fonction qui implémente cette méthode, si elle existe. Sinon, elle retourne la valeur fausse. Vous pouvez l'appeler sur une classe, un objet, ou un nom d'un paquetage. Dans ce dernier cas, elle renvoie une référence de fonction et non de méthode... mais vous ne pouvez pas faire la différence, vu que ce n'est qu'une référence...

Étant donnée une classe nommée SingeAraignee ayant une méthode appelée crier, obtenez une référence à la méthode comme ceci :

if (my $methode = SingeAraignee->can( 'crier' )) {...}

Cette technique conduit au modèle de vérification de l'existence d'une méthode avant de l'appeler :

if (my $methode = $sm->can( 'crier' )
{
# méthode; pas une fonction
$sm->$methode();
}

Utilisez can() pour tester si un paquetage met en œuvre une fonction ou une méthode spécifique :

use Class::Load;

die "Impossible de charger $module!"
unless load_class( $module );

if (my $enregistrer = $module->can( 'enregistrer' ))
{
# fonction; pas une méthode
$enregistrer->();
}

Module::Pluggable

Comme son nom l'indique, le module Class::Load du CPAN simplifie le chargement des classes. De même, le module Module::Pluggable effectue la plus grande part du travail de construction et gestion des systèmes de plugin. Familiarisez-vous avec les deux distributions.

X-F-4 - La méthode isa()

La méthode isa() reçoit une chaîne contenant le nom d'une classe ou le nom d'un type de base (SCALAR, ARRAY, HASH, Regexp, IO, et CODE). Appelez-la sur un objet comme une méthode de classe ou d'instance. isa() retourne la valeur vraie si son invoquant est ou dérive de la classe nommée, ou si l'invoquant est une référence liée au type donné.

Étant donné un objet $poivre (une référence de hachage liée à la classe Singe, qui hérite de la classe Mammifere) :

say $poivre->isa( 'Singe'    );  # affiche 1
say $poivre->isa( 'Mammifere');  # affiche 1
say $poivre->isa( 'HASH'     );  # affiche 1
say Singe->isa(   'Mammifere');  # affiche 1

say $poivre->isa( 'Dauphin'  );  # affiche 0
say $poivre->isa( 'ARRAY'    );  # affiche 0
say Singe->isa(   'HASH'     );  # affiche 0

Toute classe peut surcharger isa(). Cela peut être utile lorsque vous simulez des objets (voir Test::MockObject et Test::MockModule sur le CPAN) ou pour du code qui n'utilise pas les rôles (Rôles). Sachez que toute classe qui redéfinit isa() doit généralement avoir une bonne raison pour le faire.

Est-ce qu'une classe existe ?

Alors que UNIVERSAL::isa() et UNIVERSAL::can() sont toutes les deux des méthodes (L'équivalence méthode-fonction), vous pouvez utiliser en toute sécurité cette dernière en tant que fonction uniquement pour déterminer si une classe existe en Perl. Si UNIVERSAL::can ( $classname, 'can' ) retourne la valeur vraie, quelqu'un a défini quelque part une classe nommée $classname. Cette classe peut ne pas être utilisable, mais elle existe.

X-F-5 - Étendre UNIVERSAL

Il est tentant de stocker d'autres méthodes dans UNIVERSAL pour les rendre disponibles à tous les autres classes et objets Perl. Évitez cette tentation ; ce comportement global peut avoir des effets secondaires subtils, en particulier dans le code que vous n'avez pas écrit et ne maintenez pas, car il n'y a aucune contrainte.

Cela dit, l'abus occasionnel de UNIVERSAL à des fins de débogage et de correction des mauvais comportements par défaut peut être excusable. Par exemple, la distribution UNIVERSAL::ref de Joshua ben Jore rend utilisable l'opérateur quasi inutile ref(). Les distributions UNIVERSAL::can et UNIVERSAL::isa peuvent vous aider à déboguer des bogues en rapport avec le polymorphisme (L'équivalence méthode-fonction). Perl::Critic peut détecter ceux-ci et d'autres problèmes.

En dehors de bouts de code très soigneusement contrôlés et des situations très spécifiques, très pragmatiques, il n'y a aucune raison de mettre le code directement dans UNIVERSAL. Il y a presque toujours beaucoup de meilleurs choix de conception.

X-G - Génération de code

Les programmeurs débutants écrivent plus de code qu'ils n'ont besoin d'en écrire, en partie par manque de familiarité avec les langages, bibliothèques et idiomes, mais aussi en raison de l'inexpérience. Ils commencent par écrire de longues listes de code procédural, puis découvrent les fonctions, les paramètres, les objets, et  peut-être  les fonctions d'ordre supérieur et les fermetures.

Lorsque vous devenez un meilleur programmeur, vous allez écrire moins de code pour résoudre les mêmes problèmes. Vous utiliserez de meilleures abstractions. Vous écrirez du code plus général. Vous pouvez réutiliser le code  et quand vous pouvez ajouter des fonctionnalités en supprimant du code, vous obtiendrez quelque chose de grandiose.

Écrire des programmes qui écrivent des programmes pour vous  métaprogrammation ou génération de code  vous permet de construire des abstractions réutilisables. Alors que vous pouvez faire un énorme gâchis, vous pouvez également créer des choses étonnantes. Par exemple, les techniques de métaprogrammation rendent Moose possible (Moose).

La technique de AUTOLOAD (AUTOLOAD) pour les fonctions et les méthodes manquantes illustre cette technique sous une forme spécifique : le système de Perl de recherche des fonctions et méthodes vous permet de contrôler ce qui se passe lorsque la recherche normale de méthodes échoue.

X-G-1 - eval

La technique de génération de code la plus simple est de construire une chaîne de caractères contenant un fragment de code Perl valide et de la compiler avec l'opérateur eval sur cette chaîne. À la différence de la forme eval de bloc, qui intercepte les exceptions, la forme eval de chaîne compile le contenu de la chaîne dans la portée courante, y compris le paquetage courant et les liaisons lexicales.

Une utilisation courante de cette technique fournit une solution de repli si vous ne pouvez pas (ou ne voulez pas) charger une dépendance optionnelle :

eval { require Singe::Traceur }
or eval 'sub Singe::Traceur::log {}';

Si Singe::Traceur n'est pas disponible, ce code définit une fonction log() qui ne fera rien. Cet exemple simple est trompeur, parce qu'utiliser eval de façon correcte nécessite un peu de travail. Vous devez gérer des problèmes de guillemets lors de l'inclusion des variables au sein de votre code eval. Ajoutez plus de complexité pour interpoler certaines variables, mais pas d'autres :

sub generer_accesseurs
{
my ($methname, $attrname) = @_;

eval <<"END_ACCESSEUR";
sub get_$methname
{
my \$self = shift;
return \$self->{$attrname};
}

sub set_$methname
{
my (\$self, \$value) = \@_;
\$self->{$attrname}  = \$value;
}
END_ACCESSEUR
}

Malheur à ceux qui oublient un antislash ! Bonne chance pour convaincre votre coloration syntaxique de ce qui se passe ! Pire encore, chaque invocation de chaîne eval génère une nouvelle structure de données représentant l'ensemble du code. La compilation du code n'est pas gratuite non plus. Pourtant, même avec ses limites, cette technique est simple et utile.

X-G-2 - Fermetures paramétriques

Alors que la construction des accesseurs et mutateurs avec eval est simple, les fermetures (Fermetures) vous permettent d'ajouter des paramètres au code généré lors de la compilation sans nécessiter une évaluation supplémentaire :

sub generer_accesseurs
{
my $attrname = shift;

my $getter = sub
{
my $self = shift;
return $self->{$attrname};
};

my $setter = sub
{
my ($self, $value) = @_;
$self->{$attrname} = $value;
};

return $getter, $setter;
}

Ce code permet d'éviter les problèmes désagréables de guillemets et compile chaque fermeture une seule fois. Il utilise même moins de mémoire en partageant le code compilé entre toutes les instances de fermeture. Tout ce qui diffère est la liaison à la lexicale $attrname. Dans un processus de longue durée ou une classe avec beaucoup d'accesseurs, cette technique peut être très utile.

L'installation dans les tables de symboles est relativement facile, mais assez laide :

my ($get, $set) = generer_accesseurs( 'tarte' );

no strict 'refs';
*{ 'get_tarte' } = $get;
*{ 'set_tarte' } = $set;

Cette syntaxe bizarre d'astérisque (11), qui déréférence un hachage, renvoie à un symbole dans la table des symboles courante, qui est la section de l'espace de noms courant contenant des symboles globalement accessibles tels que des paquetages globaux, fonctions et méthodes. L'affectation d'une référence à une entrée de la table de symboles installe ou remplace cette entrée. Afin de promouvoir une fonction anonyme au rang de méthode, stockez la référence de cette fonction dans la table de symboles.

L'affectation d'une chaîne, et non d'une variable littérale, à un symbole de la table de symboles, est une référence symbolique. Vous devez désactiver la vérification des références de strict pour l'opération. De nombreux programmes ont un bogue subtil dans le code similaire, car ils font l'affectation et la génération de code en une seule ligne :

no strict 'refs';

*{ $methname } = sub {
# bogue subtil : strict refs désactivé ici également
};

Cet exemple désactive strict pour le bloc externe ainsi que pour le corps de la fonction elle-même. Or, seule l'affectation viole la vérification stricte de référence, donc désactivez les restrictions pour cette seule opération :

{
my $sub = sub { ... };

no strict 'refs';
*{ $methname } = $sub;
}

Si le nom de la méthode est une chaîne littérale dans votre code source, plutôt que le contenu d'une variable, vous pouvez l'attribuer directement au symbole pertinent :

{
no warnings 'once';
(*get_tarte, *set_tarte) =
generer_accesseurs( 'pie' );
}

L'affectation directement à la glob ne viole pas les restrictions, mais mentionner chaque glob une seule fois fait produire un avertissement « used only once » que vous pouvez désactiver avec le pragma warnings.

Tables de symboles simplifiées

Utilisez le module Package::Stash du CPAN au lieu de manipuler vous-même les tables de symboles.

X-G-3 - Manipulation lors de la compilation

À la différence du code écrit explicitement comme du code, le code généré par une chaîne eval sera compilé à l'exécution. De même, alors que vous pourriez vous attendre à ce qu'une fonction normale soit disponible pendant toute la durée de votre programme, une fonction générée dynamiquement pourrait ne pas être disponible quand vous vous y attendez.

Forcez Perl à exécuter ce code  pour générer un autre code  lors de la compilation en l'enveloppant dans un bloc BEGIN. Lorsque l'analyseur Perl rencontre un bloc étiqueté BEGIN, il analyse et compile l'ensemble du bloc, puis l'exécute (sauf s'il comporte des erreurs de syntaxe). Lorsque c'est terminé, la compilation continuera comme s'il n'y avait eu aucune interruption.

La différence entre :

sub get_age   { ... }
sub set_age   { ... }

sub get_nom   { ... }
sub set_nom   { ... }

sub get_poids { ... }
sub set_poids { ... }

... et :

sub creer_accesseurs { ... }

BEGIN
{
for my $accesseur (qw( age nom poids ))
{
my ($get, $set) =
creer_accesseurs( $accesseur );

no strict 'refs';
*{ 'get_' . $accesseur } = $get;
*{ 'set_' . $accesseur } = $set;
}
}

... est surtout une question de maintenabilité.

Dans un module, tout le code se trouvant en dehors de fonctions s'exécute lorsque vous appelez la fonction use pour charger le module, en raison de l'instruction implicite BEGIN que Perl ajoute autour du require et de l'import (Importation) exécutés par use. Tout code en dehors d'une fonction, mais à l'intérieur du module s'exécute avant que l'appel de import() ne se produise. Si en revanche vous utilisez require pour charger le module, il n'existe pas de bloc BEGIN implicite. Après la fin de la compilation du code du module, Perl exécutera le code en dehors des fonctions.

Méfiez-vous de l'interaction entre la déclaration lexicale (l'association d'un nom à une portée) et l'affectation lexicale. La première se produit lors de la compilation, tandis que la seconde a lieu à l'exécution. Ce code a un bogue subtil :

use UNIVERSAL::require;

# bogué; ne faites pas cela
my $package_voulu = 'Singe::Jetpack';

BEGIN
{
$package_voulu->require;
$package_voulu->import;
}

... car le bloc BEGIN s'exécutera avant que l'affectation de la valeur chaîne à $package_voulu ne se produise. Le résultat sera une exception à cause de la tentative d'appel de la méthode require()  (12) sur une valeur indéfinie.

X-G-4 - La classe Class::MOP

À la différence de l'installation des références de fonction pour remplir des espaces de noms et créer des méthodes, il n'y a aucun moyen simple en Perl pour créer des classes à l'exécution. Moose vient à la rescousse, avec sa bibliothèque interne Class::MOP. Il fournit un protocole métaobjet  un mécanisme pour créer et manipuler un système objet en manipulant des objets.

Plutôt que d'écrire votre propre code fragile utilisant un eval de chaîne ou d'essayer de modifier manuellement des tables de symboles, vous pouvez manipuler les entités et les abstractions de votre programme par des objets et des méthodes.

Pour créer une classe :

use Class::MOP;

my $class = Class::MOP::Class->create(
'Clef::Anglaise'
);

Ajoutez des attributs et méthodes à la classe lorsque vous la créez :

my $class = Class::MOP::Class->create(
'Clef::Anglaise' =>
(
attributes =>
[
Class::MOP::Attribute->new('$materiel'),
Class::MOP::Attribute->new('$couleur'),
]
methods =>
{
serrer    => sub { ... },
desserrer => sub { ... },
}
),
);

... ou à la métaclasse (l'objet qui représente cette classe) une fois créée :

$class->add_attribute(
experience => Class::MOP::Attribute->new('$xp')
);

$class->add_method( bash_zombie => sub { ... } );

Vous pouvez inspecter la métaclasse :

my @attrs = $class->get_all_attributes;
my @meths = $class->get_all_methods;

De même Class::MOP::Attribute et Class::MOP::Method vous permettent la création, la manipulation et l'introspection des attributs et méthodes.

X-H - Surcharge

Perl n'est pas un langage principalement orienté objet. Ses types de données standard (scalaires, tableaux et hachages) ne sont pas des objets avec des méthodes, mais vous pouvez contrôler le comportement de vos propres classes et objets, en particulier quand ils subissent une coercition ou évaluation contextuelle. Cela est la surcharge.

La surcharge est subtile, mais puissante. Un exemple intéressant est de surcharger le comportement d'un objet dans le contexte booléen. Dans le contexte booléen, un objet sera évalué à une valeur vraie, sauf si vous surchargez l'évaluation en contexte booléen.

Surcharger le contexte booléen

Pourquoi surcharger le contexte booléen ? Supposons que vous utilisez le modèle Null Object (http://www.c2.com/cgi/wiki?NullObject) pour faire évaluer à faux vos propres objets dans un contexte booléen.

Vous pouvez surcharger le comportement de l'objet pour presque chaque opération ou coercition : conversion en chaîne, en nombre, en booléen, itération, invocation, accès à un tableau, accès à un hachage, opérations arithmétiques, opérations de comparaison, correspondance intelligente, opérations binaires et même affectation. Les conversions en chaîne, en nombre et en booléen sont les plus importantes et les plus courantes.

X-H-1 - Surcharge des opérations courantes

Le pragma overload associe des fonctions aux opérations susceptibles à être surchargées en passant des paires d'arguments, dont la clé est le nom d'un type de surcharge et la valeur est une référence de fonction. Une classe Null, qui surcharge l'évaluation booléenne de sorte qu'elle soit toujours évaluée à une valeur fausse, pourrait ressembler à ceci :

package Null
{
use overload 'bool' => sub { 0 };

...
}

Il est facile d'ajouter une conversion en chaîne :

package Null
{
use overload
'bool' => sub { 0 },
'""'   => sub { '(null)' };
}

La surcharge de la coercition numérique est plus complexe, car les opérateurs arithmétiques ont tendance à être opérateurs binaires (Arité). Dans le cas de deux opérandes, les deux ayant la méthode d'addition surchargée, quelle surcharge doit être prioritaire ? La réponse doit être cohérente, facile à expliquer, et compréhensible par les personnes qui n'ont pas lu le code source de l'application.

perldoc overload essaie d'expliquer cela dans les sections intitulées Calling Conventions for Binary Operations et MAGIC AUTOGENERATION, mais la solution la plus simple consiste à surcharger la conversion en nombre (en utilisant '0+') et de dire à overload d'utiliser les surcharges fournies comme des solutions de repli si possible :

package Null
{
use overload
'bool'   => sub { 0 },
'""'     => sub { '(null)' },
'0+'     => sub { 0 },
fallback => 1;
}

L'affectation d'une valeur vraie à fallback permet à Perl d'utiliser toute autre surcharge définie pour composer l'opération demandée lorsque c'est possible. Si ce n'est pas possible, Perl agira comme s'il n'y avait aucune surcharge en vigueur. C'est souvent ce que vous voulez.

Sans fallback, Perl utilisera uniquement les surcharges spécifiques que vous avez fournies. Si quelqu'un essaie d'effectuer une opération que vous n'avez pas surchargée, Perl signalera une exception.

X-H-2 - Surcharge et héritage

Les sous-classes héritent des surcharges de leurs parentes. Elles peuvent redéfinir ce comportement de l'une des deux façons suivantes. Si la classe parente utilise la surcharge comme montré, avec des références de fonctions fournies directement, une classe enfant doit redéfinir le comportement surchargé du parent en utilisant directement overload.

Si vous écrivez une classe parente, utilisez un nom de méthode plutôt qu'une référence de fonction anonyme. Cela va permettre aux classes enfants de prouver leurs propres surcharges en redéfinissant les méthodes nommées :

package Null
{
use overload
'bool'   => 'get_bool',
'""'     => 'get_string',
'0+'     => 'get_num',
fallback => 1;
}

Toute classe enfant peut faire quelque chose de différent pour la coercition booléenne, par exemple en surchargeant get_bool().

X-H-3 - Utilisations de surcharge

La surcharge peut sembler un outil tentant à utiliser pour produire des raccourcis symboliques de nouvelles opérations, mais c'est rarement pour une bonne raison. Le module IO::All du CPAN pousse cette idée à sa limite. En retour, vous obtenez une API simple et élégante. Pourtant, pour chaque API brillante affinée grâce à l'utilisation appropriée de la surcharge, une douzaine de problèmes surgissent. Parfois, le meilleur code évite l'habileté en faveur de la simplicité.

La redéfinition de l'addition, multiplication, et même concaténation sur une classe Matrice est logique, seulement parce que la notation existante pour ces opérations est omniprésente. Un nouveau domaine de problème sans une notation établie est un mauvais candidat à la surcharge, car c'est un domaine de problème où vous devez plisser les yeux pour faire les opérateurs existants de Perl correspondre à une notation différente.

Le livre De l'art de programmer en Perl de Damian Conway suggère une autre utilisation de la surcharge : pour éviter les abus accidentels d'objets. Par exemple, une surcharge de la conversion en nombre avec croak() pour des objets qui n'ont pas de représentation numérique unique raisonnable peut vous aider à trouver et à corriger les bogues réels.

X-I - Données d'origine non fiable et le mode taint

Certaines fonctionnalités de Perl peuvent vous aider à écrire des programmes sécurisés. Ces outils ne remplacent pas la réflexion et planification approfondie, mais ils récompensent la prudence et la compréhension et peuvent vous aider à éviter les erreurs subtiles.

X-I-1 - Utilisation du mode taint

Le mode taint attache une métadonnée adhésive à toutes les données qui viennent de l'extérieur de votre programme. Toutes les données obtenues à partir des données d'origine peu fiable sont également contaminées. Vous pouvez utiliser des données contaminées au sein de votre programme, mais si vous les utilisez pour influer sur le monde extérieur  si vous les utilisez de façon non sécurisée  Perl lancera une exception fatale.

perldoc perlsec explique le mode taint en grand détail.

Lancez votre programme avec l'argument de ligne de commande -T pour activer le mode taint. Si vous utilisez cet argument sur la ligne #! d'un programme, vous devez exécuter directement le nom du programme. Si vous l'exécutez avec perl mytaintedappl.pl et omettez l'option -T, Perl se terminera avec une exception  au moment où Perl rencontre l'option sur la ligne #!, il a raté sa chance de vérifier les données de l'environnement représenté par %ENV, par exemple.

X-I-2 - Sources de contamination

La contamination peut provenir de deux sources : fichier d'entrée et environnement d'exploitation du programme. Le premier est ce que vous lisez à partir d'un fichier ou recevez de la part des utilisateurs dans le cas de la programmation Web ou un réseau. Le second comprend tous les arguments de ligne de commande, les variables d'environnement et des données provenant des appels système. Même les opérations telles que la lecture d'un descripteur de répertoire produisent des données contaminées.

La fonction tainted() du module standard Scalar::Util retourne vrai si son argument est contaminé :

die 'Oh non ! Données contaminées !'
if Scalar::Util::tainted( $valeur_suspecte );

X-I-3 - Décontamination de données

Pour décontaminer des données, vous devez en extraire certaines parties vérifiées avec des expressions régulières capturantes. Ces données capturées seront décontaminées.

Par exemple, si votre utilisateur doit saisir un numéro de téléphone américain, vous pouvez décontaminer la donnée obtenue avec :

die 'Numéro toujours contaminé !'
unless $numero =~ /(\(/d{3}\) \d{3}-\d{4})/;

my $numero_verifie = $1;

Plus votre motif d'expression régulière est spécifique sur ce que vous autorisez, plus votre programme peut être sûr. L'approche inverse qui refuse certains éléments ou formes spécifiques court le risque de négliger quelque chose de dangereux. Mieux vaut interdire quelque chose de sûr, mais inattendu, que d'accepter quelque chose de nocif qui paraît sûr. Pourtant, rien ne vous empêche d'écrire un motif capturant la totalité du contenu d'une variable, mais, dans ce cas, pourquoi utiliser le mode taint ?

X-I-4 - Décontamination de l'environnement

La variable superglobale %ENV représente les variables d'environnement du système. Ces données sont contaminées parce que des forces qui échappent au contrôle du programme peuvent y manipuler des valeurs. Toute variable d'environnement qui modifie la façon dont Perl ou le shell trouvent des fichiers et des répertoires est un vecteur d'attaque. Un programme sensible à contamination devrait supprimer plusieurs clés de %ENV et définir $ENV{PATH} à un chemin spécifique et bien sécurisé :

delete @ENV{ qw( IFS CDPATH ENV BASH_ENV ) };
$ENV{PATH} = '/path/to/app/binaries/';

Si vous ne définissez pas correctement $ENV{PATH}, vous recevrez des messages sur son insécurité. Si cette variable d'environnement contenait le répertoire de travail courant, ou si elle contenait des répertoires relatifs, ou si les répertoires spécifiés avaient des autorisations d'écriture à tout le monde, un attaquant ingénieux pourrait détourner les appels système pour commettre des méfaits.

Pour des raisons similaires, @INC ne contient pas le répertoire de travail courant en mode taint. Perl ignorera également les variables d'environnement PERL5LIB et PERLLIB. Utilisez le pragma lib ou l'option -I de la ligne de commande perl pour ajouter des répertoires de bibliothèques au programme.

X-I-5 - Les pièges et limites du mode taint

Le mode taint est tout ou rien. Il est soit actif, soit inactif. Cela amène parfois les gens à utiliser des motifs permissifs pour décontaminer des données, et donne l'illusion de sécurité. Dans ce cas, le mode taint n'offre aucune sécurité réelle. Examinez attentivement vos règles de décontamination.

Malheureusement, tous les modules ne traitent pas les données contaminées de façon appropriée. C'est un bogue que les auteurs du CPAN devraient prendre au sérieux. Si vous devez sécuriser du code hérité, envisagez l'utilisation de l'option -t, qui active le mode taint, mais fait passer les violations du mode taint du rang d'erreurs fatales à celui de simples avertissements. Ce n'est pas une solution de rechange au substitut pour le mode taint complet, mais il vous permet de sécuriser les programmes existants sans l'approche tout ou rien de -T.

XI - Perl au-delà de syntaxe

Baby Perl peut résoudre beaucoup de vos problèmes, mais il ne vous mènera pas plus loin. Les programmeurs efficaces comprennent comment interagissent et se combinent les fonctionnalités de Perl. Ce Perl fluide tire parti des caractéristiques et idiomes naturels du langage. Le résultat de la pensée perliste est un code concis, puissant et utile.

XI-A - Expressions idiomatiques

Chaque langage a un ensemble commun d'expressions ou d'idiomes. La terre tourne, mais nous parlons du soleil levant ou couchant. Nous admirons les astuces de codage habiles et grinçons des dents aux ruses trop fourbes dans les programmes.

Les expressions idiomatiques de Perl sont des caractéristiques du langage ou des techniques de conception. Elles sont les maniérismes et les mécanismes qui donnent à votre code une touche perliste. Vous n'êtes pas obligés de les utiliser, mais elles contribuent aux forces de Perl.

XI-A-1 - L'objet comme $self

Le système d'objet de Perl (Moose) traite l'invoquant d'une méthode comme un paramètre normal. Que vous appeliez une méthode de classe ou d'instance, le premier élément de @_ est toujours l'invoquant. Par convention, la plus grande partie du code Perl utilise $class comme nom de l'invoquant d'une méthode de classe et $self pour le nom de l'invoquant objet. Cette convention est suffisamment forte pour que des extensions utiles comme MooseX::Method::Signatures supposent que vous utiliserez $self comme nom d'objet invoquant.

XI-A-2 - Paramètres nommés

Perl aime beaucoup les listes. Les listes sont un élément fondamental du Perl. L'aplatissement de liste vous permet d'enchaîner plusieurs expressions pour manipuler des données de nombreuses façons afin de produire les résultats souhaités.

Alors que la simplicité du passage d'arguments de Perl (tout est aplati dans @_) est parfois trop simple, l'affectation de @_ dans un contexte de liste vous permet de déballer les paramètres nommés sous forme de paires. L'opérateur grosse virgule => (Déclarer des tables de hachage) transforme une liste ordinaire en une liste évidente de paires d'arguments :

preparer_coupe_glace(
creme_fouettee => 1,
saupoudrage    => 1,
banane         => 0,
creme_glacee   => 'pépites de chocolat à la menthe',
);

À l'intérieur de la fonction, le code déballe ces paramètres dans un hachage et traite ce hachage comme s'il était un seul argument :

sub preparer_coupe_glace
{
my %args    = @_;
my $dessert = get_creme_glacee( $args{creme_glacee} );

...
}

Hachage ou référence de hachage ?

De l'art de programmer en Perl suggère de passer plutôt des références de hachage. Cela permet à Perl d'effectuer une validation de la référence de hachage du côté de l'appelant. Autrement dit, si vous passez le mauvais nombre d'arguments, vous obtiendrez une erreur lorsque vous appelez la fonction.

Cette technique fonctionne bien avec import() (Importation) ou d'autres méthodes ; traitez autant de paramètres que vous souhaitez avant de gober tout le reste dans un hachage :

sub import
{
my ($classe, %args)    = @_;
my $paquetage_appelant = appelant();

...
}

XI-A-3 - La transformation de Schwartz

La transformation de Schwartz est une démonstration élégante de l'utilisation omniprésente des manipulations de listes (13) en Perl, un idiome emprunté à Lisp. Supposons que vous ayez un hachage Perl qui associe les noms de vos collègues avec leurs extensions téléphoniques :

my %extensions =
(
'000' => 'Damien',
'002' => 'Wesley',
'012' => 'Marcus',
'042' => 'Robin',
'088' => 'Nicolas',
);

Règles de citation de clés de hachage

La citation des clés de hachage utilisées avec la grosse virgule ne fonctionne que sur des choses qui ressemblent à des mots nus. En commençant par zéro, ces clés ressemblent à des nombres octaux. Presque tout le monde fait cette erreur.

Pour trier cette liste par ordre alphabétique des noms, vous devez trier la table de hachage par ses valeurs, et non par ses clés. Il est facile d'obtenir les valeurs triées correctement :

my @noms_tries = sort values %extensions;

... mais vous avez besoin d'une étape supplémentaire pour préserver l'association entre noms et extensions, d'où la transformation de Schwartz. Tout d'abord, convertissez le hachage en une liste de structures de données facile à trier  dans ce cas, des tableaux anonymes à deux éléments :

my @paires = map  { [ $_, $extensions{$_} ] }
keys %extensions;

sort prend la liste des tableaux anonymes et compare leurs seconds éléments (les noms) sous forme de chaînes :

my @paires_triees = sort { $a->[1] cmp $b->[1] }
@paires;

Le bloc fourni à sort reçoit ses arguments en deux variables spéciales ayant une portée (Portée) de paquetage : $a et $b. (Voir perldoc -f sort pour une analyse approfondie sur les implications de cette portée.) Le bloc sort prend ses arguments deux à la fois. Le premier devient le contenu de $a et le second le contenu de $b. Si $a devait se situer avant $b dans les résultats, le bloc doit retourner -1. Si les deux valeurs de tri ont la même position, le bloc doit retourner 0. Enfin, si $a devait venir après $b dans les résultats, le bloc doit retourner 1.

Connaissez vos données

Inverser le hachage en place fonctionnerait si personne n'avait le même nom. Cet ensemble particulier de données ne présente aucun problème, mais codez défensivement.

L'opérateur cmp effectue des comparaisons de chaînes et <=> effectue des comparaisons numériques. Une seconde opération map convertit @paires_triees en une forme plus utilisable :

my @extensions_formatees = map { "$_->[1], ext. $_->[0]" }
@paires_triees;

... et maintenant vous pouvez afficher le tout :

say for @extensions_formatees;

La transformation de Schwartz utilise les méthodes omniprésentes de traitement de listes de Perl pour se débarrasser des variables temporaires :

say for
map  { " $_->[1], ext. $_->[0]"          }
sort {   $a->[1] cmp   $b->[1]           }
map  { [ $_      =>    $extensions{$_} ] }
keys %extensions;

Lisez l'expression de droite à gauche (et de bas en haut), dans l'ordre d'évaluation. Pour chaque clé dans le hachage des extensions, faites un tableau anonyme à deux éléments contenant la clé et la valeur du hachage. Trier cette liste de tableaux anonymes par leurs seconds éléments, les valeurs du hachage. Formatez une chaîne contenant la sortie de ces tableaux triés.

Le pipeline map-sort-map de la transformation de Schwartz transforme une structure de données en une forme plus facile à trier et la retransforme ensuite en une autre forme.

Bien que cet exemple de tri soit simple, pensez au calcul d'un hachage cryptographique pour un gros fichier. La transformation de Schwartz est particulièrement utile, car elle met en cache efficacement tous les calculs coûteux en les effectuant une seule fois dans la map le plus à droite.

XI-A-4 - Lire un fichier d'un seul coup

L'opérateur local est essentiel à la gestion des variables globales magiques de Perl. Vous devez comprendre la portée (Portée) pour utiliser efficacement local, mais si vous le faites, vous pouvez utiliser des portées serrées et légères de manière intéressante. Par exemple, pour lire d'une seule traite des fichiers dans un scalaire en une seule expression :

my $file = do { local $/; <$fh> };

# ou
my $file; { local $/; $file = <$fh> };

$/ est le séparateur d'enregistrements en entrée. La localisation définit sa valeur à undef, en attente d'affectation. Comme la valeur de ce séparateur n'est pas définie, Perl lit volontiers d'un seul coup tout le contenu du descripteur de fichier. Comme un bloc do retourne la valeur de la dernière expression évaluée dans le bloc, il évalue les données lues à partir du descripteur de fichier : le contenu du fichier. À la fin de l'expression, $/ a été restaurée à son état précédent et $file contient le contenu du fichier.

Le deuxième exemple évite une deuxième copie de la chaîne contenant le contenu du fichier ; il n'est pas aussi joli, mais il utilise le moins de mémoire.

File::Slurp

Cet exemple utile est certes affolant pour les gens qui ne comprennent pas tant local que la portée. Le module File::Slurp du CPAN est une autre solution digne d'intérêt (et souvent plus rapide).

XI-A-5 - Gérer Main

Perl ne nécessite aucune syntaxe spéciale pour créer des fermetures (Fermetures). Vous pouvez fermer sur une variable lexicale par inadvertance. De nombreux programmes mettent couramment en place plusieurs variables lexicales ayant une portée de fichier avant de passer le traitement à d'autres fonctions. Il est tentant d'utiliser ces variables directement, plutôt que de passer des valeurs aux fonctions et les retourner de celles-ci, d'autant plus que les programmes grossissent. Malheureusement, ces programmes peuvent en arriver à se baser sur les subtilités de ce qui se passe pendant le processus de compilation  une variable Perl, que vous pensiez avoir initialisée à une valeur spécifique peut n'être initialisée que beaucoup plus tard.

Pour éviter ce problème, enveloppez le code principal de votre programme en une seule fonction, main(). Encapsulez vos variables dans leurs portées appropriées. Puis ajoutez une seule ligne au début de votre programme, après que vous avez utilisé tous les modules et pragmas dont vous avez besoin :

#!/usr/bin/perl

use Modern::Perl;

...

exit main( @ARGV );

sub main {
...

# successful exit
return 0;
}

Appeler main() avant toute autre chose dans le programme vous force à être explicite sur l'initialisation et l'ordre de compilation. Appeler exit avec la valeur de retour de main() empêche tout autre code hors fonction de s'exécuter.

XI-A-6 - Exécution contrôlée

La différence réelle entre un programme et un module est dans son utilisation prévue. Les utilisateurs invoquent les programmes directement, tandis que les programmes chargent des modules lorsque l'exécution a déjà commencé. Pourtant, un module et un programme sont tous les deux du code Perl. Il est facile de rendre un module exécutable. Il est de même pour faire un programme de se comporter comme un module (utile pour tester des parties d'un programme existant sans faire formellement un module). Tout ce que vous devez faire est de découvrir comment Perl a commencé à exécuter un morceau de code.

Le seul argument optionnel de la fonction caller est le nombre de contextes d'appel (Récursivité) à signaler. caller(0) fournit des informations sur le cadre de l'appel en cours. Pour permettre à un module de s'exécuter correctement comme un programme ou un module, mettez tout le code exécutable dans des fonctions, ajoutez une fonction main(), et écrivez une seule ligne au début du module :

main() unless caller(0);

S'il n'y a aucun appelant pour le module, quelqu'un l'a invoqué directement comme un programme (avec perl chemin/vers/Module.pm à la place de use Module;).

Inspection améliorée de l'appelant

Le huitième élément de la liste retourné par caller dans un contexte de liste est une valeur vraie si le contexte d'appel représente use ou require et undef autrement. Alors que c'est plus précis, peu de gens l'utilisent.

XI-A-7 - Validation postfixée des paramètres

Le CPAN offre plusieurs modules qui permettent de vérifier les paramètres de vos fonctions ; Params::Validate et MooseX::Params::Validate sont deux bonnes options. La validation simple est facile même sans ces modules.

Supposons que votre fonction prend exactement deux arguments. Vous pourriez écrire :

use Carp 'croak';

sub toiletter_singe
{
if (@_ != 2)
{
croak 'Je peux toiletter seulement deux singes !';
}
...
}

... mais dans une perspective linguistique, les conséquences sont plus importantes que la vérification et méritent d'être au début de l'expression :

croak 'Je peux toiletter seulement deux singes !' unless @_ == 2;

Cette technique de retour précoce  surtout avec les conditions postfixées  peut simplifier le reste du code. Chaque assertion de ce type représente de fait une seule ligne dans une table de vérité.

XI-A-8 - Regex en passant

De nombreux idiomes Perl s'appuient sur le fait que les expressions retournent des valeurs :

say my $ext_num = my $extension = 42;

Bien que ce code soit évidemment maladroit, il montre comment utiliser la valeur d'une expression dans une autre expression. Ce n'est pas une idée nouvelle ; vous avez probablement utilisé auparavant la valeur de retour d'une fonction dans une liste ou comme argument d'une autre fonction. Vous pouvez ne pas avoir réalisé ses implications.

Supposons que vous souhaitez extraire un prénom d'une combinaison prénom et nom avec une expression régulière précompilée dans $prenom_rx :

my ($prenom) = $nom =~ /($prenom_rx)/;

Dans un contexte de liste, une expression régulière réussie retourne une liste de toutes les captures (Capturer), et Perl attribue la première à $prenom.

Pour supprimer tous les caractères de non-mot afin de créer un nom d'utilisateur utile pour un compte système, vous pourriez écrire :

(my $nom_normalize = $nom) =~ tr/A-Za-z//dc;

/r en Perl 5.14

Perl 5.14 ajoute le modificateur de substitution non destructive /r, de sorte que vous pouvez écrire my $nom_normalise = $nom =~ tr/A-Za-z //dcr;.

Premièrement, attribuez la valeur de $nom à $nom_normalize. Les parenthèses modifient la priorité des opérations, afin que l'affectation se produise en premier. L'expression d'affectation retourne la variable $nom_normalize. Cette variable devient le premier opérande de l'opérateur de translittération.

Cette technique fonctionne sur d'autres opérateurs de modification en place :

my $age = 14;
(my $age_plus_un = $age)++;

say "J'ai $age ans, mais l'année prochaine j'aurai $age_plus_un ans";

XI-A-9 - Coercitions unaires

Le système de type de Perl fait presque toujours la chose voulue lorsque vous choisissez les opérateurs corrects. Utilisez l'opérateur de concaténation et Perl traitera les deux opérandes en tant que chaînes. Utilisez l'opérateur d'addition et Perl traitera les deux opérandes comme numériques.

Occasionnellement, vous devez donner à Perl une indication sur ce que vous voulez dire avec une coercition unaire pour forcer l'évaluation d'une valeur de manière spécifique.

Pour vous assurer que Perl traite une valeur comme numérique, additionnez-la à zéro :

my $valeur_numerique = 0 + $valeur;

Pour vous assurer que Perl traite une valeur comme un booléen, précédez-la par une double négation :

my $valeur_booleenne = !! $valeur;

Pour vous assurer que Perl traite une valeur comme une chaîne, concaténez-la à une chaîne vide :

my $valeur_string = '' . $valeur;

Bien que la nécessité de ces coercitions soit extrêmement rare, vous devez comprendre ces idiomes si vous les rencontrez. Bien qu'il puisse sembler prudent de retirer d'une expression un +0 « inutile », il se pourrait que le programme cesse de fonctionner correctement.

XI-B - Variables globales

Perl fournit plusieurs variables super globales qui sont véritablement globales, pas limitées à la portée d'un paquetage ou d'un fichier. Malheureusement, leur disponibilité globale signifie que toute modification directe ou indirecte peut avoir des effets sur d'autres parties du programme  et elles sont très concises. Les programmeurs Perl expérimentés ont mémorisé certaines d'entre elles. Peu de gens les ont toutes mémorisées. Seules quelques-unes sont utiles. perldoc perlvar contient la liste exhaustive de ces variables.

XI-B-1 - Gérer les variables super globales

Au fur et à mesure que Perl évolue, il tend à passer d'un comportement plus global à un comportement plus lexical, si bien que vous pouvez éviter beaucoup de ces variables globales. Lorsque vous ne pouvez pas les éviter, utilisez local dans la plus petite portée possible pour limiter toute modification. Vous êtes toujours sensibles à toute modification de ces variables globales par le code que vous appelez, mais vous réduisez la probabilité d'un code surprenant hors de votre portée. Comme le montre la syntaxe de lecture d'un seul coup de fichier (Lire un fichier d'un seul coup), local est souvent la bonne approche :

my $file; { local $/; $file = <$fh> };

L'effet de la localisation de $/ dure seulement jusqu'à la fin du bloc. Il existe une faible chance que du code Perl s'exécute en conséquence de la lecture des lignes du descripteur de fichier et qu'il modifie la valeur de $/ dans le bloc do. Un descripteur de fichier lié par la fonction tie (Variables liées) est l'une des rares possibilités.

Tous les cas d'utilisation de variables super globales ne sont pas si faciles à protéger, mais cela fonctionne souvent.

D'autres fois, vous avez besoin de lire la valeur d'une super globale et espérez qu'aucune autre ligne de code ne l'a modifiée. L'interception des exceptions avec un bloc eval est sensible à au moins une condition de course où l'appel de la méthode DESTROY() sur des variables lexicales sorties de leur portée peut réinitialiser $@ :

local $@;

eval { ... };

if (my $exception = $@) { ... }

Copiez $@ immédiatement après l'interception d'une exception pour préserver son contenu. Voir aussi Try::Tiny à la place (Dangers des exceptions).

XI-B-2 - Noms anglais

Le module standard English fournit des noms verbeux pour les variables super globales à ponctuation lourde. Importez-les dans un espace de noms ainsi :

use English '-no_match_vars';

Cela vous permet d'utiliser les noms verbeux documentés dans perldoc perlvar dans la portée de ce pragma.

Trois variables super globales liées aux expressions régulières ($&, $` et $') entraînent une baisse de performance globale pour toutes les expressions régulières dans un programme. Si vous oubliez l'import de -no_match_vars, votre programme sera pénalisé même si vous n'utilisez pas explicitement ces variables. Les programmes Perl modernes peuvent utiliser la variable @- à leur place.

XI-B-3 - Variables super globales utiles

La majorité des programmes Perl modernes peuvent fonctionner en utilisant seulement quelques variables super globales. Vous allez probablement rencontrer seulement quelques-unes de ces variables dans les programmes réels.

- $/ (ou $INPUT_RECORD_SEPARATOR du pragma English) est une chaîne de zéro, un ou plusieurs caractères qui indique la fin d'un enregistrement lorsque vous lisez une ligne d'entrée à la fois. Par défaut, c'est la séquence de caractère de saut de ligne spécifique à votre plate-forme. Si vous donnez à cette variable une valeur indéfinie, Perl tentera de lire lintégralité du fichier en mémoire. Si vous initialisez cette valeur avec la référence d'un nombre entier, Perl essayera de lire autant de bytes par enregistrement (alors, méfiez-vous des problèmes Unicode). Si vous définissez cette valeur à une chaîne vide (''), Perl lira un paragraphe à la fois, où un paragraphe est un morceau de texte suivi par un nombre arbitraire de sauts de ligne.

- $. ($INPUT_LINE_NUMBER) contient le nombre d'enregistrements lus à partir du descripteur de fichier le plus récemment ouvert. Vous pouvez lire le contenu de cette variable, mais lui affecter une valeur n'a aucun effet. La localisation de cette variable localisera le descripteur de fichier auquel elle se réfère.

- $| ($OUTPUT_AUTOFLUSH) détermine si Perl écrira immédiatement dans le descripteur de fichier actuellement sélectionné ou seulement lorsque la mémoire tampon de Perl est pleine. La sortie sans tampon est utile lors de l'écriture dans un pipe, un socket ou un terminal qui ne devrait pas se bloquer en attente d'une entrée. Cette variable convertira en valeurs booléennes toutes les valeurs qui lui sont affectées.

- @ARGV contient les arguments de ligne de commande passés au programme.

- $! ($ERRNO) est une dualvar (Dualvars) qui contient le résultat de l'appel système le plus récent. Dans le contexte numérique, cela correspond à la valeur errno de C, où toute autre valeur que zéro indique une erreur. Dans le contexte de chaîne, elle retourne la chaîne de caractères décrivant l'erreur système. Localisez cette variable avant de faire un appel système (implicitement ou explicitement) pour éviter d'écraser la valeur appropriée par un autre fragment de code ailleurs. La mécanique interne de Perl fait parfois des appels système, de sorte que la valeur de cette variable peut changer à votre insu. Copiez-la immédiatement après avoir provoqué un appel système pour obtenir des résultats corrects.

- $" ($LIST_SEPARATOR) est une chaîne utilisée pour séparer les éléments de tableau et de liste interpolés dans une chaîne.

- %+ contient les captures nommées des correspondances d'expressions régulières réussies (Captures nommées).

- $@ ($EVAL_ERROR) contient la valeur de l'exception la plus récente (Intercepter les exceptions).

- $0 ($PROGRAM_NAME) contient le nom du programme en cours d'exécution. Sur certaines plates-formes Unix, vous pouvez modifier cette valeur pour changer le nom du programme tel qu'il apparaît à d'autres programmes sur le système, tels que ps ou top.

- $$ ($PID) contient l'identifiant du processus de l'instance en cours d'exécution du programme, tel que compris par le système d'exploitation. Cela variera entre les programmes ayant fait un fork() et peut varier entre les threads d'exécution du même programme.

- INC détient une liste des chemins d'accès dans lequel Perl recherchera les fichiers à charger avec use ou require. Voir perldoc -f require pour les autres éléments que ce tableau peut contenir.

- %SIG mappe les signaux de bas niveau de Perl et de l'OS vers des références de fonctions utilisées pour traiter ces signaux. Bloquez l'interruption standard Ctrl-C en traitant le signal INT, par exemple. Voir perldoc perlipc pour plus d'informations sur les signaux et traitements des signaux.

XI-B-4 - Remplacer les variables super globales

Les entrées-sorties et les exceptions sont les pires coupables d'actions à distance. Utilisez Try::Tiny (Dangers des exceptions) pour vous isoler de la sémantique délicate de gestion adéquate d'exceptions. localisez et copiez la valeur de $! pour éviter les comportements étranges quand Perl effectue des appels système implicites. Utilisez IO::File et ses méthodes sur descripteurs de fichiers lexicaux (Variables spéciales de gestion des fichiers) pour limiter les changements globaux non désirés au comportement des entrées-sorties.

XII - Que faut-il éviter ?

Perl n'est pas parfait. Certaines fonctionnalités sont difficiles à utiliser correctement et d'autres semblaient très bien, mais ne fonctionnent pas si bien que cela. Quelques caractéristiques se combinent avec d'autres de façons étranges et donnent des cas limites étranges. La connaissance des aspérités de Perl vous aidera à les éviter, si possible, et à éviter les aspérités lorsque vous devez les utiliser.

XII-A - Mots nus

Perl est un langage malléable. Vous pouvez écrire des programmes dans n'importe quel style créatif, maintenable, obscur ou bizarre que vous préférez. Les bons programmeurs écrivent du code qu'ils veulent maintenir, mais Perl ne décidera pas à votre place ce que vous jugez maintenable.

Le compilateur Perl comprend les fonctions internes et les opérateurs de Perl. Il utilise des sigils pour identifier les variables et autres signes de ponctuation pour reconnaître les appels de fonctions et de méthodes. Pourtant, parfois, le compilateur doit deviner ce que vous voulez dire, en particulier lorsque vous utilisez un mot nu (bareword)  un identifiant sans un sigil ou une autre ponctuation syntaxiquement significative.

XII-A-1 - Bons usages des mots nus

Bien que le pragma strict (Pragmas) interdise à juste titre les mots nus ambigus, certains mots nus sont acceptables.

XII-A-1-a - Mots nus comme clés de hachage

Généralement, les clés de hachage en Perl ne sont pas ambiguës parce que l'analyseur syntaxique peut les identifier comme des chaînes ; flipper dans $jeux{flipper} est évidemment une chaîne.

Parfois, cette interprétation n'est pas ce que vous voulez, surtout quand vous avez l'intention d'évaluer une commande ou une fonction pour produire la clé de hachage. Pour rendre ces cas clairs, passez des arguments à la fonction ou utilisez des parenthèses, ou préfixez-les d'un plus unaire pour forcer l'évaluation de la commande :

# le 'shift' littéral est la clé
my $valeur = $elements{shift};

# la valeur produite par shift est la clé
my $valeur = $elements{shift @_}

# la fonction retourne la clé
my $valeur = $elements{myshift( @_ )}

# le plus unaire utilise la fonction shift
my $valeur = $elements{+shift};

XII-A-1-b - Mots nus comme noms de paquetage

Les noms de paquetage sont également des mots nus. Si vos conventions de nommage statuent que les noms de paquetage commencent par une majuscule et pas les fonctions, vous rencontrerez rarement des collisions de noms. Même encore, Perl doit déterminer comment analyser Package->method. Est-ce que cela veut dire « appelle une fonction nommée Package(), puis appelle method() sur sa valeur de retour » ou « appelle une méthode nommée method() de l'espace de noms Package » ? La réponse dépend du code que vous avez déjà compilé.

Forcez le compilateur à traiter Package comme un nom de paquetage en ajoutant le séparateur de paquetage (::) (même parmi les gens qui comprennent pourquoi cela fonctionne, très peu le font.) ou faites-en une chaîne littérale :

# probablement une méthode de classe
Package->method;

# certainement une méthode de classe
Package::->method;

# une méthode de classe un peu moins laide
'Package'->method;

XII-A-1-c - Mots nus comme noms de bloc de code

Les blocs de code nommés spéciaux AUTOLOAD, BEGIN, CHECK, DESTROY, END, INIT, et UNITCHECK sont des mots nus qui déclarent des fonctions sans la commande sub. Vous avez vu cela auparavant (Génération de code) :

package Singe::Butler;

BEGIN { initialiser_simiens( __PACKAGE__ ) }

sub AUTOLOAD { ... }

Alors que vous pouvez déclarer AUTOLOAD() sans utiliser sub, peu de gens le font.

XII-A-1-d - Mots nus comme constantes

Les constantes déclarées avec le pragma constant sont utilisables comme mots nus :

# n'utilisez pas ceci pour une vraie authentification
use constant NAME     => 'Bucky';
use constant PASSWORD => '|38fish!head74|';

return unless $nom eq NAME && $pass eq PASSWORD;

Ces constantes ne sont pas interpolées dans des chaînes entre guillemets doubles.

Les constantes sont un cas particulier de fonctions prototypées (Prototypes). Lorsque vous prédéclarez une fonction avec un prototype, le compilateur traitera spécialement toutes les utilisations ultérieures de ce mot nu et vous avertira sur les erreurs ambiguës d'analyse lexicale ou syntaxique. Tous les autres inconvénients de prototypes s'appliquent toujours.

XII-A-2 - Utilisations malavisées des mots nus

Même si vous codez très prudemment, les mots nus produisent toujours du code ambigu. Vous pouvez éviter les pires abus, mais vous rencontrerez plusieurs types de mots nus dans le code hérité.

XII-A-2-a - Mots nus comme valeurs de hachage

Certains programmes anciens peuvent ne pas prendre la peine de mettre entre guillemets les valeurs des paires de hachage :

# mauvais style; ne l'utilisez pas
my %parents =
(
mere => Annette,
pere => Floyd,
);

Si aucune des fonctions Floyd() et Annette() n'existe, Perl interprétera ces mots nus comme des chaînes. Le pragma strict 'subs' produira une erreur dans cette situation.

XII-A-2-b - Mots nus comme appels de fonction

Le code écrit sans strict 'subs' peut utiliser des mots nus comme noms de fonction. L'ajout des parenthèses dans l'appel de la fonction permettra au programme de satisfaire aux exigences de strict. Utilisez perl -MO=Deparse,-p (voir perldoc B::Deparse) pour découvrir comment Perl les analyse, puis mettez entre parenthèses en conséquence.

XII-A-2-c - Mots nus comme descripteurs de fichiers

Avant l'introduction des descripteurs de fichiers lexicaux (Références de descripteur de fichier), tous les descripteurs de fichiers et de répertoires utilisaient des mots nus. Vous pouvez presque toujours réécrire ce code en toute sécurité en utilisant des descripteurs de fichiers lexicaux. Le compilateur Perl reconnaît les exceptions spéciales de STDIN, STDOUT et STDERR.

XII-A-2-d - Mots nus comme fonctions de tri

Enfin, le premier opérande de la fonction interne sort peut être le nom d'une fonction à utiliser pour le tri. Bien que ce soit rarement ambigu pour le compilateur, cela peut dérouter les lecteurs humains. La possibilité de fournir une référence de fonction dans un scalaire est un peu meilleure :

# style mot nu
my @sorted = sort comparer_longueurs @unsorted;

# référence de fonction dans un scalaire
my $comparaison = \&comparer_longueurs;
my @sorted      = sort $comparaison @unsorted;

La seconde option permet d'éviter l'utilisation d'un mot simple, mais le résultat est plus long. Malheureusement, le compilateur Perl ne comprend pas la version en une seule ligne ci-dessous en raison de l'analyse spéciale de sort ; vous ne pouvez pas utiliser une expression arbitraire (par exemple, en prenant une référence à une fonction nommée) là où un bloc ou un scalaire pourraient autrement passer.

# ne fonctionne pas
my @sorted = sort \&comparer_longueurs @unsorted;

Dans les deux cas, la façon de sort d'appeler la fonction et de lui fournir des arguments peut être source de confusion (voir perldoc -f sort pour les détails). Si possible, envisagez d'utiliser à la place la forme de bloc de sort. Si vous devez utiliser une forme de fonction, ajoutez un commentaire à propos de ce que vous faites et pourquoi.

XII-B - Objets indirects

Perl n'a pas d'opérateur new. Un constructeur est quelque chose qui retourne un objet. Par convention, les constructeurs sont des méthodes de classe nommées new(), mais vous pouvez nommer ces méthodes autrement (ou même utiliser des fonctions). Plusieurs anciens tutoriels Perl OO promeuvent l'utilisation des appels du constructeur du style C++ et Java :

my $q = new CGI; # NE PAS UTILISER

... au lieu d'un appel explicite de méthode :

my $q = CGI->new;

Ces syntaxes produisent un comportement équivalent... sauf quand elles ne le font pas.

XII-B-1 - Mots nus comme appels indirects

Dans la forme d'objet indirect (ou, plus précisément, le cas datif) du premier exemple, le verbe (la méthode) précède le substantif (l'objet) auquel il se réfère. Cela est très bien dans les langues parlées, mais introduit des ambiguïtés d'analyse syntaxique en Perl.

Comme le nom de la méthode est un mot nu (Mots nus), le compilateur utilise plusieurs heuristiques pour deviner l'interprétation correcte de ce code. Bien que ces heuristiques soient bien testées et presque toujours justes, leurs modes de défaillance sont source de confusion. Les choses s'aggravent lorsque vous passez des arguments à un constructeur :

# NE PAS UTILISER
my $obj = new Classe( arg => $valeur );

Dans cet exemple, le nom de la classe ressemble à un appel de fonction. Perl peut lever l'ambiguïté de grand nombre de ces cas, mais ses heuristiques dépendent des noms de paquetage que le compilateur a vus, des mots nus qu'il a déjà résolus (et comment il les a résolus) et des noms des fonctions déjà déclarées dans le paquetage actuel. Pour une liste exhaustive de ces conditions, vous devez lire le code source du compilateur Perl  pas quelque chose que le programmeur Perl moyen veut faire.

Imaginez un problème avec une fonction prototypée (Prototypes) dont le nom se trouve par accident entrer en conflit avec le nom d'une classe ou d'une méthode appelée indirectement. Cela est rare (c'est arrivé à votre auteur en utilisant le module JSON), mais tellement désagréable à déboguer qu'il vaut mieux d'éviter les appels indirects.

XII-B-2 - Limitations scalaires de la notation indirecte

Un autre danger de la syntaxe indirecte est que le compilateur attend comme objet une expression scalaire unique. L'écriture dans un descripteur de fichier stocké dans une variable agrégat (tableau ou hachage) semble évidente, mais ne l'est pas :

# NE FONCTIONNE PAS COMME CECI
say $config->{output} 'Message de diagnostic amusant !';

Perl tentera d'appeler say sur l'objet $config.

Toutes les commandes internes fonctionnant sur des descripteurs de fichiers  print, close et say  fonctionnent d'une façon indirecte. C'était bien quand les descripteurs de fichiers étaient des variables globales de paquetage, mais les descripteurs lexicaux de fichiers (Références de descripteur de fichier) mettent en évidence les problèmes de syntaxe objet indirecte. Pour résoudre ce problème, levez l'ambiguïté de la sous-expression qui produit l'invoquant prévu :

say {$config->{output}} 'Message de diagnostic amusant !';

XII-B-3 - Alternatives à la notation indirecte

La syntaxe d'invocation directe ne souffre pas de ce problème d'ambiguïté. Pour construire un objet, appelez la méthode constructeur directement sur le nom de la classe :

my $q   = Plack::Request->new;
my $obj = Class->new( arg => $value );

Cette syntaxe présente toujours un problème de mot nu dans le sens où, si vous avez une fonction nommée Request dans l'espace de noms de Plack, Perl interprétera le nom nu de classe comme un appel à la fonction, comme ceci :

sub Plack::Request;

# vous avez écrit Plack::Reuqest->new, mais Perl a compris
my $q = Plack::Request()->new;

Même si cela arrive rarement, vous pouvez lever l'ambiguïté des noms de classe en ajoutant le séparateur de paquetage (::) ou en marquant explicitement les noms de classe comme des chaînes littérales :

# séparateur de paquetage
my $q = Plack::Request::->new;

# une chaîne littérale sans ambiguïté
my $q = 'Plack::Request'->new;

Presque personne ne fait cela.

Pour les cas limités aux opérations sur descripteurs de fichiers, l'utilisation du datif est tellement répandue que vous pouvez utiliser l'approche de l'appel indirect si vous entourez par des accolades votre invoquant prévu. Si vous utilisez Perl 5.14 ou plus récent (ou si vous chargez IO::File ou IO::Handle), vous pouvez utiliser des méthodes sur descripteurs lexicaux de fichiers. Presque personne ne le fait pour print et say.

Le module Perl::Critic::Policy::Dynamic::NoIndirect du CPAN (un plug-in pour Perl::Critic) peut analyser votre code pour trouver les appels indirects. Le module indirect du CPAN permet d'identifier et d'interdire leur utilisation dans des programmes en cours d'exécution :

# avertit sur l'utilisation des appels indirects
no indirect;

# signale une exception lors de leur utilisation
no indirect ':fatal';

XII-C - Prototypes

Un prototype est une métadonnée attachée à une fonction ou variable. Un prototype de fonction change la façon dont le compilateur Perl comprend la fonction.

Les prototypes permettent aux utilisateurs de définir leurs propres fonctions qui se comportent comme des commandes internes. Examinez la commande interne push, qui prend comme paramètres un tableau et une liste. Bien que Perl aplatisse normalement le tableau et la liste en une liste unique passée à push, l'analyseur sait traiter le tableau comme un conteneur, sans aplatir ses valeurs. En fait, tout se passe comme si on avait passé à push une référence de tableau suivie d'une liste de valeurs. Le comportement du compilateur permet à push de modifier les valeurs du conteneur.

Les prototypes de fonction sont rattachés aux déclarations de fonctions :

sub toto        (&@);
sub titi        ($$) { ... }
my  $tata = sub (&&) { ... };

Tout prototype attaché à une déclaration anticipée doit correspondre au prototype attaché à la déclaration de fonction. Perl émettra un avertissement si ce n'est pas le cas. Étrangement, vous pouvez omettre le prototype d'une déclaration anticipée et l'inclure pour la déclaration complète, mais il n'y a aucune raison de faire cela.

La commande interne prototype prend le nom d'une fonction et retourne une chaîne représentant son prototype.

Pour voir le prototype d'une commande interne, utilisez la forme CORE:: du nom de la fonction interne comme opérande de prototype :

$ perl -E "say prototype 'CORE::push';"
\@@
$ perl -E "say prototype 'CORE::keys';"
\%
$ perl -E "say prototype 'CORE::open';"
*;$@

prototype retournera undef pour les commandes internes dont vous ne pouvez pas émuler les fonctions :

say prototype 'CORE::system' // 'undef'
# undef; impossible démuler la commande system

say prototype 'CORE::prototype' // 'undef'
# undef; la commande prototype n'a pas de prototype

Vous rappelez-vous le prototype de push ?

$ perl -E "say prototype 'CORE::push';"
\@@

Le caractère @ de ce prototype représente une liste. La barre oblique inverse impose l'utilisation d'une référence de l'argument correspondant. Ce prototype signifie que push prend une référence de tableau et une liste de valeurs. Vous pourriez écrire mypush comme ceci :

sub mypush (\@@)
{
my ($array, @rest) = @_;
push @$array, @rest;
}

Parmi les autres caractères de prototypes, on trouve $ pour forcer un argument scalaire, % pour marquer un hachage (le plus souvent utilisé comme référence), et & pour identifier un bloc de code. Voir perldoc perlsub pour plus d'informations.

XII-C-1 - Le problème avec les prototypes

Les prototypes changent la façon dont Perl analyse votre code et produit une coercition sur les arguments de vos fonctions. Alors que ces prototypes peuvent ressembler superficiellement à des signatures de fonction dans d'autres langages, ils sont très différents. Ils ne documentent ni le nombre ni les types d'arguments que les fonctions attendent, ni neffectuent le mappage entre arguments et paramètres nommés.

Les coercitions de prototype fonctionnent de façon subtile, comme l'application du contexte scalaire sur des arguments reçus :

sub egalite_numerique($$)
{
my ($left, $right) = @_;
return $left == $right;
}

my @nombres = 1 .. 10;

say 'Sont égaux, quoi que cela signifie !'
if egalite_numerique @nombres, 10;

... mais fonctionnent uniquement sur des expressions simples :

sub mypush(\@@);

# erreur de compilation : prototype non concordant
# (attend un tableau, reçoit l'affectation d'un scalaire)
mypush( my $elems = [], 1 .. 20 );

Pour déboguer cela, les utilisateurs de mypush doivent connaître à la fois si un prototype existe, et les limitations du prototype de tableau.

Débogage des erreurs de prototypes

Si vous pensez que ce message d'erreur est impénétrable, attendez jusqu'à ce que vous voyez des erreurs complexes de prototypes.

XII-C-2 - Bons usages des prototypes

Les prototypes ont quelques bons usages qui l'emportent sur leurs problèmes. Par exemple, vous pouvez utiliser une fonction prototypée pour redéfinir une des commandes internes de Perl. Vérifiez d'abord si vous pouvez redéfinir la commande interne en examinant son prototype dans un petit programme de test. Ensuite, utilisez le pragma subs pour dire à Perl que vous envisagez de redéfinir une commande interne, et enfin déclarez votre redéfinition avec le prototype correct :

use subs 'push';

sub push (\@@) { ... }

Faites attention, le pragma subs reste en vigueur pour le reste du fichier, indépendamment de toute portée lexicale.

La deuxième raison d'utiliser les prototypes est de définir des constantes lors de la compilation. Lorsque Perl rencontre une fonction déclarée avec un prototype vide (par opposition à aucun prototype) et si cette fonction est évaluée à une seule expression constante, l'optimiseur transformera tous les appels à cette fonction en des constantes au lieu d'appels de fonction :

sub PI () { 4 * atan2(1, 1) }

Tout le code ultérieur utilisera la valeur calculée de pi à la place du mot nu PI ou d'un appel à PI(), en respectant la portée et la visibilité.

Le pragma standard constant gère ces détails pour vous. Le module Const::Fast du CPAN crée des scalaires constants que vous pouvez interpoler dans des chaînes.

Une utilisation raisonnable des prototypes est d'étendre la syntaxe de Perl pour fonctionner sur des fonctions anonymes en tant que blocs. Le module Test::Exception du CPAN l'utilise à bon escient pour fournir une belle API avec calcul retardé. Voir aussi Test::Fatal. Sa fonction throws_ok() accepte trois arguments : un bloc de code à exécuter, une expression régulière pour rechercher le message d'exception et une description facultative du test :

use Test::More;
use Test::Exception;

throws_ok
{ my $unobjet; $unobjet->yoink }
qr/Impossible d'appeler la méthode "yoink" sur un indéfini/,
'Une méthode sur un invoquant indéfini devrait échouer';

done_testing();

La fonction exportée throws_ok() a un prototype de &$;$. Son premier argument est un bloc qui devient une fonction anonyme. Le deuxième argument est un scalaire. Le troisième argument est facultatif.

Les lecteurs attentifs ont peut-être repéré l'absence de virgule après le bloc. C'est une bizarrerie du compilateur de Perl, qui attend un espace après un bloc prototypé, pas l'opérateur virgule. C'est un inconvénient de la syntaxe de prototype. Si cela vous gêne, utilisez throws_ok() sans profiter du prototype :

use Test::More;
use Test::Exception;

throws_ok(
sub { my $unobject; $unobject->yoink() },
qr/Impossible d'appeler la méthode "yoink" sur un indéfini/,
'Une méthode sur un invoquant indéfini devrait échouer' );

done_testing();

Une dernière bonne utilisation des prototypes est lors de la définition d'une fonction nommée personnalisée à utiliser avec sort. Ben Tilly a proposé cet exemple :

sub length_sort ($$)
{
my ($left, $right) = @_;
return length($left) <=> length($right);
}

my @sorted = sort length_sort @unsorted;

Le prototype de $$ oblige Perl à passer les paires à trier dans @_. La documentation de sort suggère que c'est un peu plus lent que l'utilisation des variables globales de paquetage $a et $b, mais l'utilisation des variables lexicales compense souvent toute perte de vitesse.

XII-D - L'équivalence méthode-fonction

Le système objet de Perl est volontairement minimal (Références liées). Parce qu'une classe est un paquetage, Perl ne distingue pas entre une fonction et une méthode stockée dans un paquetage. La même commande interne, sub, déclare les deux. Perl recherchera volontiers une fonction appelée en tant que méthode. De même, vous pouvez appeler une méthode comme si c'était une fonction  pleinement qualifiée, exportée, ou comme une référence  si vous passez votre propre invoquant manuellement.

Appeler la mauvaise chose d'une mauvaise manière provoque des problèmes.

XII-D-1 - Côté appelant

Imaginez une classe avec plusieurs méthodes :

package Commande;

use List::Util 'sum';

...

sub calculer_prix
{
my $self = shift;
return sum( 0, $self->get_items );
}

Étant donné un objet Commande $c, les appels suivants de cette méthode peuvent sembler équivalents :

my $prix = $c->calculer_prix;

# mauvais; ne pas utiliser
my $prix = Commande::calculer_prix( $c );

Bien que dans ce cas simple, ces deux appels produisent le même résultat, le second viole l'encapsulation de l'objet passant outre la recherche de méthode dans l'arbre d'héritage.

Si $c était plutôt une sous-classe ou une allomorphe (Rôles) de Commande qui redéfinissait calculer_prix(), le contournement de la recherche de méthode n'appellerait pas la bonne méthode. Toute modification de la mise en œuvre de calculer_prix(), par exemple une modification de l'héritage ou une délégation par AUTOLOAD()  pourrait conduire à l'échec du code appelant.

Perl a une situation où ce comportement peut sembler nécessaire. Si vous forcez la résolution de méthode sans recherche, comment voulez-vous invoquer la référence de la méthode qui en résulte ?

my $meth_ref = $c->can( 'appliquer_remise' );

Il existe deux possibilités. La première consiste à ignorer la valeur de retour de la méthode can() :

$c->appliquer_remise if $c->can( 'appliquer_remise' );

La seconde consiste à utiliser la référence elle-même avec la syntaxe d'appel de méthode :

if (my $meth_ref = $o->can( 'appliquer_remise' ))
{
$o->$meth_ref();
}

Lorsque $meth_ref contient une référence de fonction, Perl appellera cette référence avec $c comme invoquant. Cela fonctionne même avec le pragma strict, comme lors de l'appel d'une méthode avec un scalaire contenant son nom :

my $nom = 'appliquer_remise';
$c->$nom();

Il existe un petit inconvénient à appeler une méthode par référence ; si la structure du programme change entre le stockage de la référence et l'appel de celle-ci, la référence peut ne plus se référer à la méthode la plus appropriée. Si la classe Commande a changé de telle sorte que Commande::appliquer_remise n'est plus la bonne méthode à appeler, la référence en $meth_ref ne sera pas mise à jour.

Lorsque vous utilisez cette forme d'appel, limitez la portée des références.

XII-D-2 - Côté appelé

Comme Perl ne fait aucune distinction entre les fonctions et les méthodes au moment de leur déclaration et comme il est possible (mais déconseillé) d'appeler une fonction donnée comme une fonction ou une méthode, il est possible d'écrire une fonction appelable des deux façons.

Le module CGI a ces fonctions doubles. Chacune d'entre elles doit appliquer plusieurs heuristiques pour déterminer si le premier argument est un invoquant. Cela provoque des problèmes. Il est difficile de prédire exactement quels invoquants sont potentiellement valides pour une méthode donnée, surtout quand vous pouvez avoir affaire à des sous-classes. La création d'une API que les utilisateurs ne peuvent pas facilement mal utiliser est aussi plus difficile, sans parler de votre fardeau de documentation. Que se passe-t-il lorsqu'une partie du projet utilise l'interface procédurale et une autre utilise l'interface objet ?

Si vous devez fournir des interfaces procédurale et OO distinctes pour une bibliothèque, alors créez deux API séparées.

XII-E - Déréférencement automatique

Perl peut déréférencer automatiquement certaines références en votre nom. Étant donnée une référence à un tableau dans $arrayref, vous pouvez écrire :

push $arrayref, qw( list of values );

Étant donnée une expression qui retourne une référence de tableau, vous pouvez faire la même chose :

push $maisons{$lieu}[$placards], \@nouvelles_chaussures;

Il en va de même pour les opérateurs de tableau pop, shift, unshift, splice, keys, values et each et les opérateurs de hachage keys, values, et each. Si la référence fournie n'est pas du type approprié  si elle ne déréférence pas correctement  Perl signalera une exception. Même si cela peut sembler plus dangereux que de déréférencer explicitement les références directement, c'est en fait le même comportement :

my $ref = sub { ... };

# signalera une exception
push  $ref, qw( liste de valeurs );

# signalera aussi une exception
push @$ref, qw( liste de valeurs );

Malheureusement, ce déréférencement automatique pose deux problèmes. Tout d'abord, il ne fonctionne que sur les variables normales. Si vous avez un tableau ou un hachage élevé au rang d'objet au moyen de la fonction bless, un hachage lié à l'aide de la fonction tie, ou un objet avec surcharge de tableau ou de hachage, Perl signalera une exception à l'exécution au lieu de déréférencer la référence.

Deuxièmement, rappelez-vous que each, keys, et values peuvent fonctionner tant sur tableaux que sur hachages. Vous ne pouvez pas examiner cette ligne de code :

my @items = each $ref;

... et déterminer si @items contient une liste de paires clé/valeur ou de paires indice/valeur, car vous ignorez si $ref est une référence de hachage ou de tableau. Certes, un bon choix des noms de variable aidera, mais ce code manque intrinsèquement de clarté.

Aucun de ces inconvénients ne rend cette syntaxe inutilisable en général, mais ses contours rugueux et le risque de confusion pour le lecteur la rendent moins utile qu'elle ne pourrait l'être.

XII-F - Variables liées

Là où la surcharge (Surcharge) vous permet de personnaliser le comportement des classes et des objets pour des types spécifiques de coercition, un mécanisme appelé liaison (tying) vous permet de personnaliser le comportement des variables primitives (scalaires, tableaux, tables de hachage et descripteurs de fichiers). Toute opération que vous pouvez effectuer sur une variable liée se traduit par un appel spécifique de méthode sur un objet.

À l'origine, la commande interne tie vous autorisait à stocker des hachages et des tableaux sur l'espace disque et non en mémoire vive, ce qui permet à Perl de manipuler des volumes de données plus grands que la mémoire disponible. Le module standard Tie::File vous permet de le faire : il traite en fait les fichiers comme s'ils étaient des tableaux.

La classe à laquelle vous liez une variable doit se conformer à une interface définie pour un type spécifique de données. Lisez perldoc perltie pour un aperçu, ensuite consultez les modules standard Tie::StdScalar, Tie::StdArray et Tie::StdHash pour des détails spécifiques. Commencez par hériter d'une de ces classes, puis surchargez les méthodes spécifiques que vous devez modifier.

Collisions de noms de classe et paquetage

Si tie n'était pas assez déroutante, Tie::Scalar, Tie::Array, et Tie::Hash définissent les interfaces nécessaires pour lier scalaires, tableaux et hachages, mais Tie::StdScalar, Tie::StdArray et Tie::StdHash fournissent les implémentations par défaut.

XII-F-1 - Lier des variables

Pour lier une variable :

use Tie::File;
tie my @file, 'Tie::File', @args;

Le premier argument est la variable à lier. Le deuxième est le nom de la classe à laquelle le lier. @args est une liste optionnelle d'arguments requis pour la fonction de liaison. Dans le cas de Tie::File, @args devrait contenir un nom de fichier valide.

Les fonctions de liaison ressemblent aux constructeurs : TIESCALAR(), TIEARRAY(), TIEHASH(), ou TIEHANDLE() respectivement pour des scalaires, tableaux et hachages et descripteurs de fichiers. Chaque fonction retourne un nouvel objet qui représente la variable liée. Tant tie que tied retournent cet objet, bien que la plupart des gens utilisent tied dans un contexte booléen.

XII-F-2 - Mise en œuvre des variables liées

Pour mettre en œuvre la classe d'une variable liée, hériter d'un module standard tel que Tie::StdScalar (Tie::StdScalar ne possède pas son propre fichier .pm, alors écrivez use Tie::Scalar;), puis redéfinissez les méthodes spécifiques pour les opérations que vous souhaitez modifier. Dans le cas d'un scalaire lié, celles-ci sont probablement FETCH et STORE, éventuellement TIESCALAR(), et probablement pas DESTROY().

Voici une classe qui trace tous les accès en lecture et en écriture à un scalaire  :

package Tie::Scalar::Logged
{
use Modern::Perl;

use Tie::Scalar;
use parent -norequire => 'Tie::StdScalar';

sub STORE
{
my ($self, $valeur) = @_;
Logger->log("Stocker <$valeur> (précédemment : [$$self])", 1);
$$self = $valeur;
}

sub FETCH
{
my $self = shift;
Logger->log("Récupérer <$$self>", 1);
return $$self;
}
}

Supposons que la méthode log() de classe Logger() prend en entrée une chaîne et le nombre d'appels de fonction à remonter dans la pile d'appels pour déterminer l'endroit dans le code que l'on désire signaler.

Dans les méthodes STORE() et FETCH(), $self fonctionne comme un scalaire lié à un objet au moyen de bless. L'affectation d'une valeur à cette référence scalaire modifie la valeur du scalaire et la lecture de celle-ci retourne sa valeur.

De même, les méthodes de Tie::StdArray et de Tie::StdHash agissent respectivement sur des références liées de tableau et hachage. Encore une fois, perldoc perltie explique les méthodes que les variables liées supportent, telles que la lecture ou l'écriture simultanée de plusieurs valeurs.

tie est amusant, non ?

L'option -norequire empêche le pragma parent de tenter de charger un fichier pour Tie::StdScalar, car ce module fait partie du fichier Tie/Scalar.pm. C'est sale, mais nécessaire.

XII-F-3 - Quand utiliser les variables liées ?

Les variables liées semblent offrir des occasions amusantes de réaliser des solutions ingénieuses, mais elles peuvent produire des interfaces déroutantes. Sauf si vous avez une très bonne raison pour créer des objets se comportant comme s'ils étaient des types internes de données, évitez de créer vos propres variables liées. Elles sont souvent beaucoup plus lentes que les types de données standard.

Cela dit, les variables liées peuvent vous aider à déboguer le code délicat (utilisez le scalaire tracé de l'exemple de module Tie::Scalar::Logged donné au paragraphe ci-dessus pour vous aider à comprendre où une valeur est modifiée) ou à rendre possibles certaines choses impossibles (accéder à des fichiers très volumineux sans manquer de mémoire). Les variables liées sont moins utiles en tant qu'interfaces primaires d'objets ; il est souvent trop difficile et trop contraignant d'essayer d'adapter l'ensemble de votre interface à celle soutenue par tie().

Un dernier mot d'avertissement est une triste mise en accusation de la programmation paresseuse : beaucoup de modules se plient en quatre pour empêcher l'utilisation des variables liées, ne serait-ce que par accident. C'est dommage, mais le code de la bibliothèque est parfois rapide ainsi et paresseux avec ce qu'il attend, et vous ne pouvez pas toujours y remédier.

XIII - Qu'est-ce qui manque ?

Perl n'est pas parfait, mais il est malléable  ne serait-ce que parce qu'aucune configuration n'est idéale pour tous les programmeurs et tous les usages. Certaines fonctionnalités sont disponibles dans les modules standard de Perl. D'autres, plus nombreuses, sont disponibles sur le CPAN. Les programmeurs Perl efficaces profitent pleinement des options à leur disposition.

XIII-A - Défauts manquants

Le processus de conception du langage Perl a toujours essayé de combiner l'utile à l'évolutivité, mais il était impossible de prédire en 1994 l'avenir tel qu'il est aujourd'hui en 2014. Perl 5 a élargi le langage et a rendu le CPAN possible, mais il a également conservé la compatibilité ascendante avec la plupart des programmes Perl écrits avec des versions antérieures, et ce jusqu'à Perl 1 en 1987.
Le meilleur code Perl de 2014 est très différent de celui des années 1994 ou 1987.
Bien que Perl inclue une bibliothèque standard étendue, elle n'est pas exhaustive. Plusieurs des meilleurs modules sont disponibles à l'extérieur du Perl standard, à partir du CPAN (Le CPAN LINK interne). La métadistribution Task::Kensho comprend plusieurs autres distributions qui intègrent ce qu'il y a de mieux sur le CPAN. Lorsque vous avez besoin de résoudre un problème, commencez par aller y jeter un coup d'œil.
Cela dit, il y a néanmoins beaucoup de vrais joyaux dans les modules standard de Perl.

XIII-A-1 - Le pragma strict

Le pragma strict (Pragmas) vous permet d'interdire ou de réactiver différentes constructions linguistiques qui présentent le risque d'utilisation abusive.

strict interdit les références symboliques, nécessite la déclaration des variables (Portée lexicale) et interdit l'utilisation des « mots nus » (bareword) non déclarés (Mots nus). Bien que les références symboliques soient parfois nécessaires (Utiliser et importer), l'utilisation d'une variable comme nom de variable rend possibles des erreurs subtiles d'action à distance ou, pire encore, la possibilité d'une validation insuffisante de données saisies par l'utilisateur manipulant des données privées à des fins malveillantes.

Exiger des déclarations de variables permet de détecter les fautes de frappe dans les noms de variables et encourage une utilisation judicieuse de la portée des variables lexicales. Il est plus facile de voir la portée prévue d'une variable lexicale si toutes les variables ont les déclarations my ou our dans la portée appropriée. Le pragma strict a lui-même une portée lexicale. Voir perldoc strict pour plus de détails.

XIII-A-2 - Le pragma « warnings »

Le pragma warnings (Gérer les avertissements) contrôle la détection des diverses classes d'avertissements. Il vous prévient si vous tentez de transformer en chaîne de caractères la valeur undef ou d'utiliser le mauvais type d'opérateur sur des valeurs. Il met également en garde sur l'utilisation des fonctions obsolètes.

Les avertissements les plus utiles expliquent que Perl a eu du mal à comprendre ce que veut dire la syntaxe que vous avez employée et a dû deviner la bonne interprétation. Même si Perl devine souvent correctement, la résolution des ambiguïtés vous assurera une exécution correcte de vos programmes.

Le pragma warnings a une portée lexicale. Voir perldoc perllexwarn ou perldoc warnings pour plus de détails.

Si vous utilisez à la fois les warnings et les diagnostics, vous obtiendrez des explications plus complètes pour chaque avertissement émis par Perl sur vos programmes, tout droit sorties de perldoc perldiag. C'est une grande aide pour votre apprentissage de Perl, mais veillez à désactiver diagnostics avant de déployer votre programme, pour éviter que vous ne remplissiez vos journaux ou ne révéliez des informations de débogage aux utilisateurs.

XIII-A-3 - IO::File et IO::Handle

Avant Perl 5.14, les descripteurs de fichiers lexicaux étaient des objets de la classe IO::Handle, mais vous deviez charger IO::Handle explicitement avant de pouvoir appeler les méthodes de cette classe. Depuis Perl 5.14, ces descripteurs de fichiers sont instanciés depuis la classe IO::File et Perl la charge pour vous.

Ajoutez use IO::Handle; à votre code exécuté sous Perl 5.12 ou moins si vous faites appel aux méthodes de ces descripteurs.

XIII-A-4 - Le pragma « autodie »

Perl vous laisse le soin de traiter les erreurs d'exécution (ou de ne pas le faire). Si vous oubliez de vérifier la valeur de retour de chaque appel à open() par exemple, vous pourriez vous retrouver à essayer de lire un descripteur de fichier fermé  ou pire, à risquer de perdre des données que vous souhaitiez écrire. Le pragma autodie change ce comportement pour vous. Si vous écrivez :

use autodie;

open my $fh, '>', $file;

… un échec de la fonction open() signalera une exception. Étant donné que l'approche la plus appropriée à un appel système qui a échoué est de lancer une exception, ce pragma vous économise du code bateau répétitif et vous permet d'avoir l'esprit tranquille si vous ne testez pas la valeur de retour. Voir perldoc autodie pour plus d'informations.

XIII-A-5 - Les numéros de version Perl

Si vous rencontrez du code Perl sans savoir quand il a été écrit ou qui l'a écrit, pouvez-vous dire quelle est la version de Perl requise ? Si vous avez beaucoup d'expérience avec Perl, avant ou après la version Perl 5.10, vous pourriez vous souvenir de la version de Perl qui a ajouté la fonction say et quand autodie est entré dans les modules standard de Perl. Sinon, vous pourriez avoir à deviner, éplucher les fichiers de la documentation perldelta ou utiliser CPAN::MinimumVersion depuis le CPAN.

Il n'y a aucune obligation à ajouter à tous les nouveaux programmes que vous écrivez le numéro de la version minimum requise de Perl, mais cela peut clarifier vos intentions. Par exemple, si vous avez testé votre code avec Perl 5.12 et utilisez uniquement des fonctionnalités présentes dans Perl 5.12, écrivez :

use 5.012;

… ce qui documentera votre intention. Vous rendrez plus simple aux outils automatiques l'identification des fonctionnalités particulières de Perl dans votre code à utiliser ou non. Si quelqu'un utilise plus tard votre programme et prouve qu'il fonctionne sous Perl 5.10, vous pourrez changer le numéro de version sur la base de cette preuve concrète.

XIV - Remerciements Developpez

Nous remercions chromatic de nous avoir aimablement autorisés à publier cet article. Cet article est une traduction autorisée dont le texte original peut être trouvé sur onyxneon.com ou sur modernperlbooks.com :

Nous remercions aussi Mishulyna et Laurent Rosenfeld pour la traduction, Laurent Rosenfeld pour sa relecture technique ainsi que ClaudeLELOUP pour sa relecture orthographique, sans oublier djibril pour l'initiative du projet de traduction et la mise en forme du tutoriel.

Les commentaires et les suggestions d'amélioration sont les bienvenus, alors, après votre lecture, n'hésitez pas.

Commentez

Veuillez ne pas confondre le livre « Modern Perl » de chromatic avec un autre livre Perl intitulé : « Perl moderne - L'essentiel des pratiques actuelles » de Sébastien Aperghis-Tramoni, Philippe Bruhat, Damien Krotkine, Jérôme Quelin.

(1)C'est début 2014 que chromatic a rédigé cette version de son livre. On est arrivé à Perl 5.20 au moment où cette traduction française est préparée (fin 2014). Mais, bien sûr, ce texte s'appuie au plus sur la version 5.18 (NdT).

(2)« Il y a plus d'une façon de le faire. »

(3)Le réseau mondial d'archives Perl.

(4)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 €.

(5)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)

(6)En programmation fonctionnelle, utiliser une application partielle pour réduire le nombre d'arguments passés à une fonction fréquemment invoquée s'appelle souvent « curryfier » la fonction (ce terme provient du nom du mathématicien américain Haskell Curry, dont le prénom a par ailleurs servi à baptiser un langage de programmation fonctionnelle). (NdT)

(7)Mais attention, soyez prévenu, on peut certes lire ce livre gratuitement en ligne, mais il y a un sérieux risque que cela vous coûte en définitive de l'argent : le livre Higher Order Perl de Dominus est tellement intéressant que vous allez peut-être vous sentir obligé d'acquérir une version papier pour mieux l'explorer. (NdT)

(8)Dans son livre De l'art de programmer en Perl (aux éditions O'Reilly, traduction française du livre Perl Best Practices), Damian Conway, l'un des gourous les plus respectés de la communauté Perl, est nettement plus péremptoire à l'encontre d'AUTOLOAD() : « N'utilisez pas AUTOLOAD() ». (NdT)

(9)Certains théoriciens universitaires des grammaires formelles préfèrent utiliser en français le terme « expressions rationnelles » pour traduire « regular expressions ». Dans la pratique, les informaticiens francophones utilisent dans leur immense majorité le terme « expressions régulières ». En outre, les expressions régulières de Perl ont tellement été étendues et enrichies (par rapport aux « expressions rationnelles » de la théorie des langages formels) qu'elles sont loin d'être « rationnelles » au sens théorique de ce mot dans ce contexte. On conservera donc ici le terme « expressions régulières ». (NdT)

(10)Le tableau @ISA d'un paquetage ou d'une classe n'a rien à voir avec le diminutif du prénom féminin. C'est une contraction des mots anglais « is a » (« est un » ou « est une »). Si une classe fille Rectangle hérite d'une classe mère Quadrilatere, le tableau @ISA dit simplement qu'un rectangle « est un » quadrilatère. Bien entendu, le tableau @ISA peut contenir le nom de plusieurs classes parentes en cas d'héritage multiple (mais seulement si vous avez vraiment envie de vous tirer une balle dans le pied avec des héritages multiples). (NdT)

(11) Pensez-y comme à un sigil de typeglob, où typeglob est un terme du jargon Perl pour la « table de symboles » (NdA).

(12) UNIVERSAL::require ajoute une méthode require() à UNIVERSAL (NdA).

(13)Voir aussi à ce sujet la première partie consacrée aux opérateurs de listes du tutoriel sur la Programmation fonctionnelle en Perl.