Apprendre Perl rapidement, 2e partie

David Bourgais

Bienvenue à nouveau dans ce tutoriel où vous découvrirez le langage Perl.
Dans la première partie, nous avions vu ensemble les trois variables fondamentales de Perl. À présent, nous irons plus loin dans la compréhension de ce langage pour nous faciliter la vie dans nos développements. Ainsi, nous apprendrons à partager notre code pour ne plus le rendre « monolithique », à découvrir des nouvelles boucles et à faire prendre des décisions à nos programmes. Enfin, nous allons voir quelles sont quelques variables dites « spéciales » que Perl met à notre disposition pour nous faciliter la vie. Naturellement, vous pourrez avoir plus de compléments sur ces différents points via notre rubrique Perl, la FAQ et bien sûr, le forum. Commentez

- Titre : Apprendre Perl rapidement, 2e partie

- Auteur : David Bourgais

- Parution : 22 juillet 2005

- Mise à jour : 24 novembre 2019

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 GLDavid. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Conditions en Perl

- II. Répéter une opération, les boucles

- III. Variables spéciales de Perl

- IV. Écrire des fonctions

I - Conditions en Perl

Il peut être bien utile, dans un programme, de prendre des décisions en fonction des variables et des valeurs que vous leur donnerez. Et comme dirait l'adage :  « avec des si, on mettrait Paris en bouteille ! ». Alors, appliquons ce proverbe dans le code suivant :

condition1.pl

#!/usr/bin/perl

print "Rentre une ville :\n";
$ville = <stdin>;
chomp $ville;
if ($ville eq "Paris") {
print $ville." rentre dans une bouteille !\n";
}

Voyons ce code. La première ligne vous est bien connue, il s'agit de la ligne shebang où nous déclarons le chemin de l'interpréteur Perl. Puis, nous affichons avec print une chaîne de caractères. La ligne suivante demande à l'utilisateur de rentrer une valeur, ici, le nom d'une ville qui sera intégré dans la variable scalaire $ville. La fonction chomp supprime le retour chariot contenu dans la variable $ville.
Maintenant, vient notre propos. Le programme se pose la question suivante :  « Qu'est-ce que je fais en fonction de la valeur de $ville ? » À cela, nous lui apportons la solution : « si » la valeur de $ville est égale à Paris, alors, tu affiches la chaîne concaténée entre la valeur de $ville et une phrase. Notez que nous mettons la solution à la condition entre une paire d'accolades ouvrante-fermante que l'on appelle un bloc.
Mais, j'entends votre question : et si l'utilisateur ne rentre pas Paris ? Et bien, nous allons voir un autre mot clé bien utile si un « if » est insuffisant. Il s'agit du mot else soit le « sinon » anglais. Prenons l'exemple suivant :

Condition2.pl

#!/usr/bin/perl

print "Rentre un chiffre pour connaître son inverse :\n";
$nombre = <stdin>;
chomp $nombre;
if($nombre == 0){
print "La division par 0 est impossible !";
}
else {
$resultat = 1/$nombre;
print "L'inverse de ".$nombre." est ".$resultat."\n";
}

Après avoir écrit notre ligne shebang, nous demandons à l'utilisateur de rentrer un nombre et nous enlevons le retour chariot. Comme nous divisons 1 par ce nombre pour obtenir l'inverse du nombre, il est évident qu'une division par 0 est impossible. Aussi, à l'aide de la paire if-else, nous allons analyser le nombre rentré. Dans notre « if », nous regardons si le nombre est égal à 0. Si c'est le cas, nous affichons une remontrance envers l'utilisateur (si, si, parfois, l'informaticien se montre sévère envers ses utilisateurs) et le programme se termine. Dans le cas contraire, le bloc else est alors invoqué : nous stockons le résultat dans la variable scalaire $resultat de la division de 1 par notre nombre $nombre. Enfin, nous affichons le résultat de cette opération dans une chaîne concaténée.
Mais, Perl va encore plus loin ! En effet, ce langage propose même un mot clé contraire à « if ». Il s'agit du mot clé unless (à moins que en français). Considérons le code suivant qui reprend le précédent :

Condition3.pl

#!/usr/bin/perl

print "Rentre un chiffre pour connaître son inverse :\n";
$nombre = <stdin>;
chomp $nombre;
unless($nombre == 0){
$resultat = 1/$nombre;
print "L'inverse de ".$nombre." est ".$resultat."\n";
}
else {
print "La division par 0 est impossible !";
}

Notre condition se lit donc de la manière suivante : à moins que $nombre soit égal à 0, on procède à la division. Sinon, on affiche l'erreur. Vous aurez compris la subtilité : if se contente de vérifier si la condition est vraie alors qu'unless vérifie si la condition est fausse. Bien naturellement, vous devez à chaque appel d'une condition placer un bloc (paire d'accolades ouvrante-fermante) à la suite de votre condition. Enfin, une dernière remarque pour les tests de comparaison. Vous aurez vu que j'ai utilisé deux opérateurs différents pour tester une variable scalaire qui contient une chaîne de caractères et une autre qui contient un nombre. Retenez ainsi que si vous voulez comparer une égalité entre chaînes de caractères, vous devez avoir recours à l'opérateur eq (pour équivalent). Son opposé est ne (pour non équivalent), toujours pour les chaînes de caractères. Concernant les nombres, nous retrouvons des opérateurs connus : == (pour l'égalité, attention de ne pas utiliser un = seul !), <, > (pour les différences strictes), <=, >= (pour les différences strictes ou égales) parmi les plus usités.

II - Répéter une opération, les boucles

Dans le cours précédent, nous avons commencé à voir quelques boucles, mais sans rentrer dans les détails. Maintenant, rentrons dans les détails ! Mais, à quoi sert une boucle ? Une boucle va vous aider à répéter un bloc d'instructions jusqu'à ce qu'elle satisfasse une condition. Toutefois, il existe aussi des cas où des boucles seront infinies ! C'est le cas des IHM (Interfaces Homme-Machine).
Mais reprenons notre propos. Comme toujours, nous allons nous aider des traductions anglais => français pour comprendre une boucle. Débutons avec la boucle while. La boucle while se réalise tant que la condition n'est pas atteinte. Un petit exemple :

boucle1.pl

#!/usr/bin/perl

print "Table de multiplication de 2\n";
$i = 0;
while($i<=10){
print "$i*2 = ".($i*2)."\n";
$i++;
}

Vous aurez compris que nous allons afficher la table de multiplication de 2. Nous initialisons une variable scalaire $i à 0. Cette variable va être utilisée dans notre boucle. Notre boucle while nous dit ceci : « tant que $i est inférieur ou égal à 10, j'effectue ce que l'on me demande ». La boucle while est suivie alors d'une paire d'accolades ouvrante-fermante contenant les instructions de la boucle (ce qu'elle doit faire). Ainsi, dans le bloc ou corps de boucle, nous demandons à afficher la valeur de $i actuelle et de la multiplier par 2. Enfin, nous incrémentons $i. L'incrémentation consiste à ajouter 1 à une valeur donnée. Il existe deux types d'incrémentations :

- la préincrémentation : ++$i; où la valeur sera d'abord incrémentée ;

- la postincrémentation : $i++; où la valeur sera après incrémentée.

Enfin, il existe l'opérateur de décrémentation (--) qui se base sur les mêmes règles que l'incrémentation. Notre boucle se termine lorsque sa valeur atteint 10.

En fait, comme tout langage, Perl vous autorise à écrire une boucle selon votre préférence. Par exemple, la boucle for permet de rassembler en 1 ligne l'initialisation d'une variable, sa condition et son incrémentation. La boucle for signifiant « pour », reprenons notre exemple de tout à l'heure :

boucle2.pl

#!/usr/bin/perl

print "Table de multiplication de 2\n";
for ($i=0; $i<=10; $i++){
print "$i*2 = ".($i*2)."\n";
}

Un code plus court, n'est-ce pas ? L'utilisation d'un for n'est guère différente de la boucle while. La boucle for nous dit : « Pour $i égal à 0 et tant que $i est inférieur ou égal à 10, j'incrémenterai de 1 $i et je ferai ce que l'on me dit de faire ».

Dans notre partie consacrée aux conditions, nous avons vu qu'il existait un opposé à if nommé unless. Comme dans la nature, il existe un opposé pour chaque élément, la boucle while a aussi son opposé : until. Les boucles while et for se réalisent tant que la condition est vraie, la boucle until se réalise tant que sa condition est fausse. En anglais, until se traduit par « jusqu'à ce que ». Voyons comment utiliser until avec notre exemple de table de multiplication :

Boucle3.pl

#!/usr/bin/perl

print "Table de multiplication de 2\n";
$i = 0;
until($i>10){
print "$i*2 = ".($i*2)."\n";
$i++;
}

Le code n'est pas si différent de notre exemple avec while. Mais la pensée est différente. Comme dans notre exemple de boucle1.pl, nous initialisons une scalaire $i à 0. Mais notre boucle nous indique cette fois : « Jusqu'à ce que $i soit supérieur strictement à 10, je fais ce que l'on me dit ». Le corps de la boucle ne change pas. Seule la condition a été modifiée.

Mais certaines boucles sont bien utiles pour des types de variables plus évoluées comme les listes ou tables de hachages. Ainsi, la boucle foreach est très pratique pour parcourir les éléments d'une liste. Cependant elle peut aussi manipuler aisément une table de hachage :

Boucle4.pl

#!/usr/bin/perl

@liste = (0,1,2,3,4,5,6,7,8,9,10);
print "Table de multiplication de 2\n";
foreach $i (@liste){
print "$i*2 = ".($i*2)."\n";
}

Nous avons ainsi initialisé une liste avec une série de chiffres. Puis la boucle indique « Pour chaque élément noté $i de la liste @liste (écrite entre parenthèses), je fais ce que l'on me dit ». Le corps de la boucle multiplie ainsi l'élément en cours par 2 et affiche le résultat.

III - Variables spéciales de Perl

Perl contient quelques variables réservées qui sont bien utiles notamment pour manipuler les paramètres d'appels d'un script, rapporter une erreur ou connaître son environnement. Loin de nous est l'objectif de dresser une liste exhaustive de ces différentes variables, nous nous contenterons de vous citer les plus utiles.
Commençons par quelques variables scalaires spéciales. Savez-vous qu'il existe une variable scalaire par défaut ? Celle-ci est notée $_. Elle contient tout ce qui est en cours d'une boucle où elle est la variable par défaut de nombreuses fonctions.

Special1.pl

#!/usr/bin/perl

@liste = (0,1,2,3,4,5,6,7,8,9,10);
print "Table de multiplication de 2\n";
foreach (@liste){
print "$_*2 = ".($_*2)."\n";
}

Vous connaissez ce code, il s'agit de l'affichage de la table de multiplication de 2 à l'aide d'une boucle foreach. Mais vous me direz qu'il manque une variable. Pas du tout ! Dans notre ligne où nous décrivons notre boucle foreach, la variable spéciale $_ peut être écrite explicitement sinon elle est appelé implicitement par le code. Notre code aurait pu s'écrire de la manière suivante avec le même résultat :

Special2.pl

#!/usr/bin/perl

@liste = (0,1,2,3,4,5,6,7,8,9,10);
print "Table de multiplication de 2\n";
foreach $_ (@liste){
print "$_*2 = ".($_*2)."\n";
}

Il est aussi parfois bien utile de rapporter une erreur. La variable $! sert ainsi à enregistrer l'état de votre erreur :

special3.pl

#!/usr/bin/perl

print "Rentres un nom de fichier :\n";
$fichier = <stdin>;
chomp $fichier;
if (-f $fichier){
print "$fichier existe.\n";
}
else {
print "$!\n";
}

À l'aide de ce code, nous demandons au script de vérifier si le fichier dont le nom est rentré par l'utilisateur existe. Si c'est le cas (avec l'option -f d'un if, nous testons l'existence d'un fichier dont le nom est contenu dans une variable scalaire), le script affiche que le fichier existe. Dans le cas contraire, nous affichons le contenu de notre erreur. Si vous rentrez un nom de fichier qui n'existe pas, $! contiendra alors la valeur :

No such file or directory

Il existe aussi des listes spéciales à Perl. Aussi, vous savez que de nombreux programmes ont besoin de paramètres pour pouvoir s'exécuter. Un exemple sur les machines Unix est que certaines commandes vous affichent de l'aide lorsqu'elles n'ont pas d'arguments ou que ceux-ci sont inexacts. Aussi, comment pouvons-nous tester l'existence de paramètres pour un programme Perl. Tous les paramètres invoqués par un script Perl sont ainsi contenus dans un tableau spécial, le tableau @ARGV. Sa manipulation est identique à tous les tableaux que vous créerez en Perl :

Special4.pl

#!/usr/bin/perl

foreach (@ARGV){
print $_;
}

Dans ce simple code, nous voulons afficher la liste des paramètres passés à notre programme. Comment passe-t-on un argument ? Quand vous appelez votre script, il suffit d'écrire à sa suite les arguments :

perl special4.pl toto tata tutu

Ainsi, notre programme special4.pl vous affichera toto, tata puis tutu. Ceci est particulièrement utile quand vous passez à votre programme un nom de fichier :

special5.pl

#!/usr/bin/perl

foreach $file (@ARGV){
if (-f $file){
print "OK, $file existe.\n";
}
else {
print "$file : $!\n";
}
}

Enfin, il existe des listes associatives ou tables de hachages spéciales. Nous citerons ainsi %ENV qui contient ainsi tous les renseignements sur votre système :

Special6.pl

#!/usr/bin/perl

print "Liste des informations de votre système :\n";
while (($cle, $valeur) = each (%ENV)){
print "$cle a comme valeur $valeur.\n";
}

Naturellement, cette table de hachage se manipule comme toute bonne table de hachage :

special7.pl

#!/usr/bin/perl

print "Votre processeur est un $ENV{'PROCESSOR_IDENTIFIER'} :\n";

IV - Écrire des fonctions

Pour le moment, nous nous sommes contentés d'écrire du code monolithique soit qui tient en un seul bloc. Cependant, dans les langages modernes (dont Perl), il est souvent bien utile de compartimenter notre code en des blocs qui auront des tâches bien spécifiques à remplir. Ces blocs sont appelés des fonctions. Ces fonctions peuvent prendre ainsi des arguments, traiter ensuite ces éventuels arguments et peuvent aussi rendre un résultat. Moralité, les informaticiens répondent à l'adage « Diviser pour mieux régner ».
L'écriture d'une fonction n'est guère compliquée. Vous devez ainsi déclarer votre fonction précédée du mot clé sub (pour sub-function, en anglais sous-fonction), suivi ensuite d'une paire ouvrante-fermante d'accolades. Celles-ci définiront le corps de la fonction soit les instructions que votre fonction devra traiter. Prenons un exemple simple :

fonction1.pl

#!/usr/bin/perl

sub fonction{
print "La fonction est appelée.\n";
}

#Corps principal de notre script
print "Début de notre script.\n";
fonction();
print "C'est tout pour aujourd'hui.\n";

Ainsi, notre script débutera toujours par son corps principal. En l'occurrence, notre code affichera d'abord la chaîne de caractères « Début de notre script. ». Puis, nous appelons notre fonction par son nom suivi d'une paire de parenthèses. Ces parenthèses contiendront les arguments passés à notre fonction. Ici, nous n’avons rien à transmettre donc, nous laissons notre paire de parenthèses vide. La fonction ainsi appelée va donc faire ce pourquoi elle a été créée : afficher la chaîne de caractères « La fonction est appelée. ». Enfin, le corps principal de notre fonction reprend la main et affiche la dernière chaîne "C'est tout pour aujourd'hui.\n".

Nous avons vu que nous pouvons transmettre des arguments à une fonction via la paire de parenthèses invoquées lors de l'appel. Ainsi, prenons l'exemple suivant :

fonction2.pl

#!/usr/bin/perl

sub inverse{
if ($_[0] == 0){
print "Division par 0 impossible !\n";
}
else {
$resultat = 1/$_[0];
print "L'inverse de $_[0] est $resultat.\n";
}
}

print "De quel nombre voulez-vous avoir l'inverse ?\n";
$nombre = <stdin>;
chomp $nombre;
inverse($nombre);

Dans ce code, nous demandons à l'utilisateur de rentrer un nombre. Celui-ci est stocké dans la variable scalaire $nombre. Puis, cette scalaire $nombre sera transmise à la fonction inverse dans la paire de parenthèses. Venons-en à notre fonction inverse. Notre nombre est contenu dans une variable nommée $_[0] qui est testée dans un if pour vérifier si, bien entendu, ce nombre n'est pas égal à 0. Eh oui, nous avons une nouvelle variable spéciale ! Cette variable est en réalité le tableau @_ qui contient tous les paramètres transmis à une fonction. Le tableau spécial @_ peut aussi bien contenir des variables scalaires, des nombres, des chaînes de caractères et même des tableaux et des tables de hachages. Vous pouvez tout transmettre à une fonction. Dans le cas où notre valeur est égale à 0, la fonction répond par un message d'erreur. Dans le cas contraire, elle procède au calcul de l'inverse et renvoie le résultat.

Enfin, nous avons vu qu'il était tout à fait possible qu'une fonction renvoie un résultat. Une fonction peut très bien renvoyer un résultat qui soit une variable scalaire, une liste ou une table de hachage. Reprenons notre calcul d'inverse :

fonction3.pl

#!/usr/bin/perl

sub inverse{
if ($_[0] == 0){
print "Division par 0 impossible !\n";
}
else {
$resultat = 1/$_[0];
return $resultat;
}
}

print "De quel nombre voulez-vous avoir l'inverse ?\n";
$nombre = <stdin>;
chomp $nombre;
$reponse = inverse($nombre);
print "L'inverse de $nombre est $reponse.\n";

Dans le corps principal de notre script, nous appelons certes notre fonction, mais vous remarquerez que nous demandons à ce que le résultat de cette fonction soit contenu dans la variable scalaire $reponse. Notre fonction va donc tester la valeur de son argument (est-elle égale à 0 ou non) et dans le cas où celui-ci n'est pas égal à 0, la fonction procède au calcul de l'inverse. Ce calcul est contenu dans la variable $resultat. Puis, la fonction va retourner la valeur de $resultat au corps principal de notre script par l'intermédiaire du mot clé return suivi de la variable à retourner (return se traduit par retourne en anglais). Notre script reprend la main avec la valeur $reponse égale au calcul de l'inverse du nombre demandé à l'utilisateur et nous affichons le résultat.

Cette manière d'écrire son code rend plus simple la lecture et permet aussi au programmeur de réutiliser des fonctions suffisamment génériques dans d'autres codes. D'où un intérêt dans la simplification de résolution d'un problème et une portabilité des solutions apportées.

À bientôt pour notre dernier cours d'initiation à Perl ;)

@++