Un programme de concordance
On utilisera les programmes déjà écrits auxquels on ajoutera les modifications nécessaires pour l'objectif visé.
Perl :
apprentissage 5
1.1. Formats
daffichage
Perl
dispose d'un mécanisme de formatage des données pour
générer par exemple des rapports ou des graphismes.
Le
mot-clé format permet de déclarer un format et le mot-clé
write permet de l'exécuter. Les formats sont liés à un
handle de fichier et on y accède implicitement par la fonction write.
Les formats, comme les packages et les sous-programmes, sont declarés
plutôt qu'exécutés. De ce fait, ils peuvent
apparaître à n'importe quel endroit d'un programme. En
général, on les regrouper tous ensemble.
Le
nom du format associé par défaut à un handle de fichier
donné est le même que celui de ce dernier. Le format par
défaut de STDOUT est donc nommé STDOUT, et le format par
défaut du handle de fichier FILE est également appelé FILE.
Les
formats de sortie sont déclarés comme suit:
format
NOMDEFORMAT =
FORMATLISTE
Si
NOMDEFORMAT est omis, c'est le format STDOUT qui est défini.
Pour
afficher un résultat en utilsant un format dimpression, il
faut :
- définir
la variable système $~ au format désiré :
$~ = "NOMDEFORMAT" ;
- appeler
la fonction write :
write ;
FORMATLISTE
comprend une séquence de lignes, chacune d'elles peut être de
trois types:
- Un
commentaire, indiqué en mettant un # en première colonne.
- Une
ligne "image", donnant le format pour une ligne de sortie.
- Une
ligne d'arguments fournissant les valeurs à insérer dans la ligne
d'image précédente.
Les
lignes images sont imprimées exactement comme elles apparaissent
à part certains champs auxquels sont substitués des valeurs.
Chaque
champ de substitution commence par @ ou par ^.
Le
champ @ représente le type normal de champ.
Le
champ ^ est utilisé pour un remplissage rudimentaire de blocs de texte
multilignes.
La
longueur du champ est indiquée en remplissant le champ par plusieurs
<, > ou | :
- <
: pour spécifier une justification à gauche,
- >
: pour spécifier une justification à droite,
- |
: pour spécifier une justification au centre.
Si
la variable excède la largeur specifiée, elle est tronquée.
Le
champ spécial peut être utilisé pour imprimer des valeurs
non tronquées sur plusieurs lignes ; il doit généralement
apparaître seul sur une ligne image.
Les
valeurs sont spécifiées sur la ligne suivante dans le même
ordre que les champs image. Les expressions qui fournissent les valeurs doivent
être séparées par des virgules. Les expressions sont
évaluées dans un contexte de liste avant que la ligne ne soit
traitée, et une seule expression de liste peut produire plusieurs
éléments. Les expression peuvent remplir plusieurs lignes si
elles sont mises entre accolades. Dans ce cas, l'accolade ouvrante doit
être le premier token de la première ligne.
Les
champs image qui commencent par ^ au lieu de @ sont traités de
manière spéciale. Avec un champ #, celui-ci est mis à
blanc si la valeur est indéfinie. Pour les autres types, le ^ autorise
une certaine forme de mode de remplissage. Au lieu d'une expression quelconque,
la valeur fournie doit être un nom de variable scalaire contenant
une
chaîne de caractères. Perl met tout le texte qu'il peut dans le
champ, puis coupe le début de la chaîne afin que, la fois
suivante, le reste du texte puisse être imprimé. On utilise
normalement une séquence de champs dans une pile verticale pour afficher
un bloc de texte. On peut terminer le champ final par le texte "...", qui
apparaît en sortie si le texte est trop long pour être
entièrement édité.
Les
champs ^ peuvent produire des enregistrements de longueur variable. Si le texte
à formater est court, il suffit de répéter quelques fois
la ligne de format avec le champ ^. Pour des données peu abondantes,
cela donne quelques lignes blanches. Un tilde placé n'importe où
sur la ligne de format supprime celles-ci (le tilde lui-même est
transformé en espace en sortie). Deux tildes côte à
côte répètent la ligne jusqu'à ce que tous les
champs de cette ligne aient été imprimés.
Le
traitement des en-têtes de formats est géré par
défaut par un format dont le nom est identique à celui du handne
de fichier courant, auquel TOP est ajouté. Il est
déclenché au début de chaque page.
Exemple
:
Dans
le programme de concordance n°2, on trouve le code suivant :
format STDOUT_TOP =
Mot n°Ligne # Contenu Ligne
.
format STDOUT =
@<<<<<<<<<<< @>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$::Mot, $::Ligne, $::Lignes[$::Ligne]
.
1.2. Notions
de package
Perl
fournit un mécanisme qui protège différentes sections de
code d'une altération accidentelle par leurs variables respectives. En
fait, à part un certain nombre de variables magiques, il n'existe pas
vraiment d'élément ressemblant à une variable globale en
Perl. Le code est toujours compilé dans le package courant. Le package
courant initial est le package main, ou package principal, mais vous pouvez
passer à tout moment de l'un à l'autre en utilisant la
déclaration package. Le package en cours determine la table de symboles
employée pour la vérification des noms (pour les noms qui ne sont
pas autrement qualifiés par un package). La notion de package courant
est un concept concernant à la fois la compilation et
l'exécution. La plupart des vérifications de nom surviennent
à la compilation, mais les vérifications d'exécution se
produisent quand des références symboliques sont
déréférencées et quand de nouveaux bouts de code
sont analysés sous eval. En particulier, les opérations eval
savent de quel package elles ont été invoquées, et
propagent ce package vers l'intérieur comme le package courant du code
évalué.
La
portée d'une déclaration package commence à la
déclaration elle-même jusqu'à la fin du bloc le plus
interne (ou jusqu'à une autre déclaration package de même
niveau, qui cache le précédent.) Une déclaration de
package est à portée lexicale, en dépit du fait qu'un
package constitue une entité globale. Mais une déclaration de
package se contente de déclarer l'identité du package par
défaut pour le reste du bloc l'encadrant. Les noms de variables qui ne
sont ni déclarés, ni qualifiés sont recherchés dans
ce package.
Tous
les identificateurs ultérieurs (à l'exception de ceux qui sont
déclarés avec my, ou ceux qui sont qualifiés par un nom de
package différent) sont placés dans la table de symboles
appartenant au package. On met habituellement une déclaration package en
premier dans un fichier destiné à être inclus par require
ou use. Mais il s'agit encore une fois d'une convention. Il est possible
d'insérer une déclaration package partout où l'on peut
mettre une instruction. On peut même la mettre à la fin d'un bloc,
auquel cas elle n'aurait aucun effet. On peut passer dans un package par plus
d'un endroit, ce qui influence essentiellement le choix de la table de symboles
utilisée par le compilateur pour le reste du bloc. C'est ainsi qu'un
package donné peut se répartir sur plus d'un fichier.
Vous
pouvez vous référer aux identificateurs dans les autres packages,
en préfixant l'identificateur avec le package et un double deux-points:
$Package: :Variable. Si le nom de package est nul, c'est le package principal
qui est pris en compte. Cela étant, $::variable équivaut à
$main::variable.
Les
packages peuvent être imbriqués dans d'autres packages, ce que
l'on note par $EXTERNE::INTERNE::variable.
Seuls
les identificateurs (les noms commencant par des lettres ou par des signes de
ponctuation) sont rangés dans la table de symboles du package en cours.
Tous les autres symboles sont conservés dans le package principal,
notamment toute les variables magiques à ponctuation seule comme ~! et $.
Les
identificateurs STDIN, STDOUT, STDERR, ARGV, ARGVOUT, ENV, INC, et SIG sont
contraints de se trouver dans le package principal même quand ils sont
utilisés à d'autres fins que ce à quoi ils étaient
destinés.
Tables
de Symboles
La
table de symbole d'un package est rangée dans un hachage dont le nom est
le même que celui du package complété d'un double
deux-points. Le nom de la table de symboles principale est de ce fait %main::,
ou %::, puisque
le
package par défaut est le principal.
Le
nom de la table de symboles principale contient toutes les autres tables de
symboles de plus haut niveau, y compris elle-même, et %EXTERNE::INTERNE::
est donc aussi %main::EXTERNE::INTERNE::.
Les
clefs d'un hachage de table de symboles sont les identificateurs des symboles
de la table. Les valeurs d'un hachage de table de symboles sont les valeurs
typeglob correspondantes. Donc, quand on utilise la notation typeglob *nom, on
n'accède en fait qu'à une valeur dans le hachage qui contient la
table de symbole du package en cours. Et la démarche suivante a le
même effet, bien que la première soit plus efficace en raison de
la recherche dans la table de symboles au moment de la compilation:
local
*symbole = *main::variable;
local
*symbole = $main::{"variable"};
Puisqu'un
package est un hachage, on peut en rechercherr les clefs, et à partir de
l toutes ses variables.
foreach
Snomsym (sort keys %main::) {
local
*sym = $main::{$nomsym};
print
"\$$nomsym est defini\n" if defined $sym;
print
"\@$symname est defini\n" if defined @sym;
print
"\%$nomsym est defini\n" if defined %sym;
}
Comme
tous les packages sont accessibles (directement ou indirectement) par
l'intermédiaire du package principal, chaque variable du package est
visible dans le programme par du code Perl. C'est exactement ce que fait le
débogueur Perl quand on lui demande de visualiser toutes les variables.
L'assignation
à un typeglob effectue une opération de création d'alias ;
c'est-à-dire que
*dick
= *richard;
rend
tout accessible par l'intermédiaire de l'identificateur richard pour
être également accessible par le biais du symbole dick. S'il ne
s'agit que de créer un alias pour une variable particulière ou un
sous-programme, il suffit d'assigner une référence:
*dick
= \$richard;
$richard
et $dick deviennent une même variable mais @richard et @dick restent des
tableaux différents.
Constructeurs
et destructeurs de package: BEGIN et END
Deux
définitions spéciales de sous-programme qui fonctionnent comme
constructeurs et destructeurs sont les routines BEGIN et END. Le sub est
optionnel pour ces routines.
Un
sous-programme BEGIN est exécuté dès que possible,
c'est-à-dire dès qu'il est complètement défini, et
même avant que le reste du fichier le contenant soit analysé. On
peut trouver plusieurs blocs BEGIN à l'intérieur d'un fichier;
ils s'exécutent dans l'ordre de leur définition. Comme un bloc
BEGIN s'exécute immédiatement, il peut entraîner des
définitions de sous-programmes provenant d'autres fichiers en temps
utile pour qu'elles deviennent visibles au moment de la compilation du reste du
fichier. Ceci est important parce que les déclarations de sous-programme
changent la façon dont le reste du fichier sera analysé. Au
minimum, la déclaration d'un sous-programme l'autorise à
être utilisé comme un opérateur de liste, sans
parenthèses. Et si le sous-programme est déclaré avec un
prototype, les appels peuvent être analysés comme ceux que l'on
fait aux nombreuses fonctions intégrées (selon le type de
prototype utilisé).
Un
sous-programme END, par contraste, est exécuté aussi tard que
possible, ce qui signifie quand l'interpréteur sort, même si cette
sortie résulte d'une fonction die, ou d'une exception
généree en interne, comme quand on essaie d'appeler une fonction
indéfinie (mais pas si elle surgit de nulle part par un signal; il faut
l'intercepter par soi-même (si possible)).
On
peut trouver plusieurs blocs END à l'intérieur d'un fichier; ils
s'exécutent dans l'ordre inverse de leur définition.
1.3. Modules
Un
module n'est qu'un package réutilisable qui est défini dans un
fichier de bibliothèque dont le nom est le même que le nom du
package (avec un .pm à la fin). Un module peut fournir un
mécanisme pour exporter quelques-uns de ses symboles dans la table de
symboles de n'importe quel package qui l'utilise. n peut également
fonctionner comme une définition de classe et rendre ses
opérations disponibles de facon implicite par le biais d'appels de
méthode sur la classe et ses objets, sans exporter explicitement de
symbole. Il peut aussi faire un peu des deux.
La
plupart des modules exportateurs se basent sur la sémantique
d'exportation habituelle fournie par le module Exportateur. Par exemple, pour
créer un module d'exportation appelé TAL, créez un fichier
appelé Fred.pm et mettez ceci au début:
package
TAL;
require
Exporter;
@ISA
= qw(Exporter);
@EXPORT
= qw(funcl func2);
@EXPORT_OK
= qw($mot1 @listemots %concordance func3);
Continuez
ensuite à déclarer et à utiliser vos variables dans les
fonctions sans aucune qualification.
Les
modules Perl sont inclus dans votre programme en écrivant:
use
Module;
use
Module LISIE;
Ceci
pré-charge le Module au moment de la compilation, puis en importe les
symboles que vous avez demandés de manière implicite ou
explicite. Si l'on ne fournit pas une liste de symboles dans une LISTE, alors
la liste du tableau @EXPORT du module est utilisée (et si vous
fournissez une LISTE, tous les symboles doivent être mentionnés
soit dans @EXPORT, soit dans @EXPORT_OK, faute de quoi une erreur en
résultera). Les deux déclarations ci-dessus sont exactement
équivalentes à:
require
"Module.pm";
Module->import();
require
"Module.pm";
Module->import(LISTE);
Tous
les modules Perl ont l'extension .pm. use ainsi que require le prendront en
compte (tout comme les apostrophes doubles) pour ne pas devoir écrire
"Module.pm". Cela aide à différencier les nouveaux modules des
fichiers .pl et .ph utilisés par les versions précédentes
de Perl. Les noms de modules sont aussi capitalisés à moins
qu'ils
ne fonctionnent comme des pragmas. Les pragmas sont en effet des directives de
compilation, et ce type de module est parfois appelé module pragmatique.
Comme
la déclaration use (sous toutes ses formes) implique un bloc BEGIN, le
module est chargé (et son code d'initialisation est lancé)
dès que la déclaration use est compilée, avant que le
reste du fichier soit compilé. C'est ainsi que use peut fonctionner
comme un mécanisme de pragma pour changer le comportement du
compilateur, et que les modules sont capables de déclarer des
sous-programmes qui deviennent alors visibles en tant qu'opérateurs de
liste (non qualifiés) pour le reste du fichier courant. Si, d'un autre
côté, l'on invoque require à la place de use, il faut
explicitement qualifier toute invocation de routines à
l'intérieur du package requis.
En
général, use est plus recommandé que require, parce que
l'on obtient les messages d'erreur plus rapidement. Mais require est utile pour
aller chercher les modules à l'exécution sans se fatiguer.
Les
packages Perl peuvent être hébergés à
l'intérieur d'autres packages, ce qui fait qu'on peut avoir des noms de
package qui contiennent "::". Mais ces noms composés ne conviennent pas
comme noms de fichiers sur de nombreux systèmes.