Initiation au Langage Perl


Suivant Précédent

  • Séance 5

  • Objectif
  • 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 d’affichage


    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 d’impression, il faut :

    1. définir la variable système $~ au format désiré :

    $~ =  "NOMDEFORMAT" ;

    1. appeler la fonction write :

    write ;

    FORMATLISTE comprend une séquence de lignes, chacune d'elles peut être de trois types:

    1. Un commentaire, indiqué en mettant un # en première colonne.
    2. Une ligne "image", donnant le format pour une ligne de sortie.
    3. 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 | :


    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.