Nous entamons ici une série d'articles sur la programmation en Perl. Ce langage très riche et puissant est une boîte à outils fort utile dans de nombreuses situations : administration système, manipulation de textes (mail, logs, linguistique, génétique), programmation réseau (CGI, mod_perl, etc), bases de données, interfaces graphiques etc. Ses nombreuses bibliothèques le rendent vite irremplaçable aux yeux de ceux qui en acquièrent la maîtrise. La prise en main du langage est facilitée par de nombreux rapprochements possibles avec le C, le shell ou awk.
Cette série d'articles a la délicate ambition de s'adresser à la fois au programmeur débutant et à celui qui connaîtrait bien le C ou le shell. Que le premier me pardonne de faire des comparaisons avec d'autres langages et de taire peut-être certains points qui me semblent évidents. Que le second m'excuse de passer à son goût trop de temps à expliquer des notions qui lui semblent simples ; les choses se corseront dans les articles à venir ...
Ce premier article aborde des notions importantes en Perl et nécessaires pour bien comprendre la suite. Vous serez sans doute un peu déçu de ne pas faire des choses extrêmement puissantes immédiatement, mais patience : qui veut aller loin ménage sa monture.
Pour vous mettre en appétit, voici un petit exemple de la concision de Perl et de sa puissance :
my @r = qw(Un programme Perl est 5 fois plus rapide a ecrire); map { tr/A-Z/a-z/; s/\d//g; } @r; foreach (sort grep !/^$/, @r) { print "$_\n"; }
Ce programme créé une liste de mots (la phrase de la première ligne), transforme les majuscules de ces mots en minuscules, supprime les chiffres appartenant aux mots, supprime les mots vides et affiche la liste des mots ainsi transformés dans l'ordre lexical. Et dites-vous que vous aurez en main toutes les notions nécessaires à sa compréhension dès le second article ...
Perl est un langage de haut niveau, qui a la prétention de combiner les avantages de plusieurs autres langages. Première facilité, il gère lui même la mémoire (ramasse-miettes, pas de limite de buffers, pas d'allocation à faire etc). De plus, les tableaux, les listes et les tables de hachage sont natifs, ils sont intégrés à la grammaire même du langage. Récursivité, modularité, programmation objet, accès au système et au réseau, interface avec le C, avec (g)Tk, avec Apache sont aussi au menu. Et souvenez-vous que l'une des devises de Perl est : there is more than one way to do it (il y a plus d'une façon de le faire).
Il existe principalement deux types de langages : les langages compilés
et les langages interprétés. Pour les premiers (on retrouve par exemple
dans cette catégorie le C et le C++), il existe deux phases distinctes :
la compilation des sources par un compilateur (gcc
par
exemple) puis l'exécution du programme ainsi obtenu par le système.
Pour les seconds (les shells par exemple), il n'y a pas de phase de
compilation, un interpréteur va lire le code et directement agir en fonction
de celui-ci.
Perl est un langage à la fois interprété et compilé. Il n'y a pas de phase
intermédiaire de compilation car l'interpréteur (qui se nomme
perl
en minuscules, alors que le langage prend une
majuscule) compile le code sans que le programmeur ne s'en rende compte,
puis l'exécute.
L'interpréteur se charge donc à la fois de la compilation et de l'exécution.
Il existe trois façon distinctes de faire tourner un programme Perl :
Mettre du code en ligne de commande. On peut écrire ceci dans un shell :
perl -w -e 'print("Salut Larry\n");'
Le programme perl
est lancé avec du code Perl comme
argument, directement sur la ligne de commande (option -e
).
En anglais, on appelle cela un one-liner (pas de traduction établie en français,
peut-être monoligne, uniligne, soliligne ...). Le code sera alors exécuté :
la fonction print
affiche son argument.
Salut Larry
Ce n'est pas la façon la plus courante d'écrire du Perl, mais c'est une manière facile et rapide de faire un petit calcul ou de faire appel à une fonction Perl.
L'option -w
, que l'on retrouvera tout au
long de l'article, est positionnée dans le but que l'interpréteur affiche des
messages d'avertissement (warnings) à différents propos : il indique les
variables utilisées une seule fois ou utilisées avant d'être
initialisées, il signale les redéfinitions de fonctions, etc.
Pour marquer les esprits, on pourrait faire un parallèle avec l'option
-Wall
de gcc
pour ceux qui connaissent.
Je vous conseille donc de toujours utiliser cette option.
La seconde manière de faire est de créer un fichier salut.pl
contenant :
print("Salut Larry\n");
Puis de lancer la commande suivante depuis un shell :
perl -w salut.pl
Le programme perl
est lancé avec le nom du fichier
en argument. Il va alors lire le contenu de ce fichier et l'interpréter
comme du code Perl.
La troisième façon de faire est de créer un fichier salut2.pl
contenant :
#!/usr/bin/perl -w print("Salut Larry\n");
La première ligne est le shebang, bien connu
des habitués des scripts en shell. Cette ligne (qui doit toujours
être la première du fichier) indique au système d'exploitation le
chemin de l'exécutable à lancer pour interpréter ce fichier.
Le premier caractère doit être un dièse, ce qui a pour effet que cette
ligne est considérée comme un commentaire Perl par l'interpréteur.
Ensuite un point d'exclamation. Puis le chemin absolu
vers l'exécutable perl
(à adapter selon votre installation,
voir ce que répond type perl
ou which perl
).
Enfin les options que l'on souhaite passer à l'interpréteur.
Il faut maintenant rendre ce fichier exécutable et le lancer :
chmod +x salut2.pl ./salut2.pl
Grâce à la ligne de shebang, le système le reconnaît donc comme un programme nécessitant un interpréteur pour être exécuté. Cet interpréteur est lancé avec pour paramètres les options fournies dans le shebang ainsi que le nom du fichier.
Cette dernière façon de faire est sans doute la plus courante dès que l'on écrit un programme qui sera utilisé plusieurs fois.
Avant de continuer, quelques commentaires sur la syntaxe Perl. Excepté dans les chaînes de caractères, la présence ou l'absence d'espaces, de sauts de ligne ou de tabulations ne change pas le comportement du programme, l'indentation est libre. Comme on vient de le voir, une instruction Perl est terminée par un point-virgule. Les fonctions peuvent prendre leurs arguments entre parenthèses (comme en C) ; il faut aussi savoir que l'on peut se passer de ces parenthèses. On aurait pu écrire :
print "Salut Larry\n";
Les deux syntaxes sont tout à fait valides. Dans les cas simples (appel d'une fonction avec un ou deux paramètres), on pourra omettre les parenthèses. Dans le cas de combinaisons plus complexes (plusieurs fonctions appelées en paramètre d'autres fonctions, listes en argument...), je vous conseille de les mettre pour lever toute ambiguïté possible.
Un commentaire commence par un dièse (#
)
et se termine en fin de ligne (comme en shell). On constitue un bloc d'instructions
en les regroupant dans des accolades {}
comme en C.
Des exemples suivront.
Perl est un langage faiblement typé, ce qui signifie qu'une donnée n'aura pas spécialement de type : les nombres, les chaînes de caractères, les booléens etc seront tous des scalaires et ne seront différenciés que par leur valeur et par le contexte de leur utilisation.
En Perl, il existe principalement trois structures de données : les scalaires, les tableaux et les tables de hachage. Chaque structure de données est liée à un caractère spécial (lire la suite).
Un scalaire est une donnée atomique. Par exemple, ce pourrait être une
chaîne de caractères (suite de caractères) ou un nombre (entier ou flottant).
On verra plus tard que les références (c'est-à-dire les pointeurs de
Perl) sont des scalaires, même s'ils sont un peu spéciaux.
Les variables scalaires sont précédées d'un dollar ($
) :
$x
est donc une variable scalaire.
Les tableaux permettent de stocker plusieurs scalaires en les indiçant.
De la même façon qu'en C, on pourra demander le ième élément d'un
tableau, i étant un entier. L'accès à un élément sera en temps
constant, il ne dépendra pas de son indice dans le tableau.
Les variables de type tableau sont précédées d'un arobase
(@
) : @t
est donc une variable de type tableau. Lorsque l'on utilise la
syntaxe @t
, on désigne la totalité du tableau ;
si nous voulons parler du ième élément, nous allons manipuler
un scalaire, il faudra donc préfixer par un dollar : $t[4]
est l'élément d'indice 4 dans le tableau @t
,
les crochets []
délimitant l'indice
(nul besoin de mettre l'arobase, Perl sait grâce aux crochets qu'il
s'agit d'un élément de tableau).
Une table de hachage en Perl est une structure de données permettant
d'associer un scalaire à un autre ; on parle de clefs et de valeurs :
une valeur est associée à une clef. Naturellement, dans une même
table de hachage les clefs sont uniques ; les valeurs, par contre,
peuvent être tout à fait quelconques. Les variables de type table de hachage
sont précédées d'un caractère pourcent (%
) : %h
est donc une variable de type table de hachage. De la même façon que
pour les tableaux, %h
represente la totalité de la table de hachage ;
accéder à un élément se fera avec un dollar : $h{uneclef}
est l'élément de clef uneclef
de la table de hachage %h
,
les accolades {}
délimitant la clef.
Fonctionnellement, on pourrait voir une table de hachage comme un tableau
dont les indices peuvent être non-numériques.
Nous reviendrons sur ces types de données tout au long des articles.
Chaque opération en Perl est évaluée dans un contexte spécifique. La façon dont l'opération se comportera peut dépendre de ce contexte. Il peut jouer un rôle important sur le type des opérandes d'une expression et/ou sur le type de sa valeur. Il existe deux contextes principaux : le contexte scalaire et le contexte de liste.
Par exemple, une affectation d'une expression à une variable de type scalaire évaluera cette expression dans un contexte scalaire ; de la même façon, une affectation à une variable de type liste évaluera le membre droit en contexte de liste. Autre exemple que je détaillerai un peu plus loin, les opérateurs de test imposent un contexte précis à leurs opérandes.
Certains opérateurs et fonctions savent dans quel contexte ils sont appelés
et renvoient un scalaire ou une liste selon ce contexte d'appel.
La fonction grep
en est un bon exemple (nous verrons
cela lorsque nous parlerons des listes).
On peut forcer le contexte d'une expression au contexte scalaire
en utilisant la fonction scalar()
.
Il existe plusieurs contextes scalaires : le contexte de chaînes de caractères, le contexte numérique et le contexte tolérant. Par exemple une addition impose un contexte numérique à ses deux opérandes ; cela signifie que les opérandes sont transformés en nombres quels que soient leur type et leur valeur (reste ensuite au programmeur à savoir ce que vaut une liste ou une chaîne quelconque en contexte scalaire...). Le contexte de chaînes est un contexte où, comme son nom l'indique, les scalaires seront considérés comme des chaînes de caractères. Le contexte tolérant n'est ni de chaînes ni numérique, il est simplement scalaire.
Il existe aussi un contexte vide (void context in english)
qui correspond au fait que la valeur d'une expression est ignorée.
C'est par exemple le contexte utilisé lorsque l'on appelle une fonction
sans récupérer sa valeur de retour, comme l'appel à la fonction
print
dans la section précédente.
Ce contexte n'apporte pas grand chose au programmeur, mais permet
à l'interpréteur Perl appelé avec l'option -w
de prévenir en cas d'usage d'une expression sans effet de bord
en contexte vide (c'est-à-dire une expression qui ne fait rien et dont on ne
se sert pas). Par exemple, la ligne d'instructions suivante :
"Bonjour";
provoque le message suivant :
Useless use of a constant in void context at prog.pl line 5.
Vous aurez l'occasion de manipuler les contextes tout au long de cette introduction au langage. Même si cela n'est pas forcément explicite à chaque instant, le contexte est souvent important.
Comme dit précédemment, une variable scalaire peut contenir une chaîne de
caractères (String
en Java et autres) ou un nombre
(entier ou nombre à virgule flottante : int
ou
float
en C, C++ etc) ; je ne rentrerai pas dans
l'explication de ce "ou".
Voici des exemples de scalaires corrects :
12 "texte" 'texte' -3.14 3e9
Contrairement au C où le caractère \0
de code ASCII 0
(zéro) est le marqueur de fin de chaîne, en Perl les chaînes de caractères
peuvent sans souci contenir ce caractère : "a\0f"
est
une chaîne comportant 3 caractères. On aura donc aucun mal à traiter des
fichiers binaires en Perl.
Les chaînes de caractères ont, comme en shell, principalement
deux délimiteurs possibles :
les doubles quotes ("
) et les simples quotes
('
). Elles n'ont pas le même rôle :
Dans une chaîne délimitée par des doubles quotes, le contenu est interprété :
"Bonjour\n"
est une chaîne suivie d'une fin de
ligne. De la même manière "\t"
est une tabulation
(il existe d'autres caractères spéciaux).
Dans "Bonjour $prenom"
la variable
$prenom
est substituée par
son contenu ; c'est-à-dire que ce scalaire contiendra la chaîne
Bonjour
, suivie d'une espace, suivie du contenu
de la variable $prenom
.
S'il faut accoler un texte immédiatement après une variable, on
utilisera les accolades pour délimiter le nom de la variable ; par
exemple dans "il ${prefixe}donn$suffixe"
, c'est
bien la variable $prefixe
qui sera utilisée, puis
la chaîne donn
et enfin la variable
$suffixe
. On notera que ces accolades n'ont rien
à voir avec celles des tables de hachage.
Certains caractères doivent être "protégés" avec un anti-slash
(\
) si on veut les faire apparaître telsquels dans
la chaîne de caractères ; ce sont les quatre suivants : " $ @ \
.
La chaîne "\$v"
ne contient donc pas la valeur d'une
supposée variable $v
mais contient le caractère
dollar et le caractère v
.
Dans une chaîne délimitée par des simples quotes, aucune interprétation du contenu n'a lieu :
'Bonjour\n'
est une chaîne comportant les
caractères B o n j o u r \
et
n
, c'est-à-dire 9 caractères
(notez que '\n'
comporte 2 caractères).
La chaîne 'Bonjour $prenom'
ne comporte pas le
contenu d'une hypothétique variable $prenom
mais
le caractère dollar suivi de la chaîne prenom
.
Puisque les variables ne sont pas substituées, les caractères à
protéger sont moins nombreux ; seuls '
et
\
ont besoin d'être précédés d'un anti-slash
pour apparaître tels-quels dans une chaîne délimitée par de simples
quotes.
Les nombres n'ont quant à eux pas besoin de délimiteurs pour
être manipulés : $x = 10.2
affecte le nombre 10,2
à la variable $x
.
En Perl, il n'est pas obligatoire de déclarer les variables. Par défaut,
l'usage d'une variable la crée ; si c'est un scalaire, elle aura la
valeur undef
(lire plus loin) ; s'il s'agit d'une
liste ou une table de hachage, elle sera vide.
Pour d'évidentes raisons de relecture et pour éviter des erreurs bêtes,
je vous conseille de toujours déclarer vos variables avant de les
utiliser (sauf peut-être dans le cas de scripts de quelques lignes).
Pour déclarer une variable, il nous faut utiliser my
:
my $x; my $y = 10; my $z = "hello";
Nous venons ici de déclarer trois variables scalaires. Ces variables seront visibles (accessibles) dans toute la suite du bloc ainsi que dans les sous-blocs (comme en C) ; comme on s'y attend, elles ne le seront par contre pas dans les fonctions appelées depuis ces blocs. Le placement des déclarations est libre dans le bloc (comme en C++), il n'est pas nécessaire de les mettre en début de bloc.
Voici quelques exemples d'utilisation de variables (on suppose qu'elles sont déjà déclarées) :
$x = $y + 3; $prenom = "Jules"; $phrase = "Bonjour $prenom"; print("$phrase\n");
Cette dernière ligne affichera à l'écran Bonjour
Jules
suivi d'un caractère de nouvelle ligne. Les habitués du
shell noterons bien qu'une variable est toujours précédée de son dollar
même si elle est à gauche d'un égal d'affectation.
undef
C'est une valeur particulière signifiant «non-défini». C'est
aussi la valeur par défaut des variables scalaires non initialisées :
my $x;
est équivalent à my
$x=undef;
On peut affecter cette valeur à une variable après
son initialisation : $x=undef;
ou
undef($x);
Si l'on veut tester qu'une variable scalaire vaut ou non
undef
, il faut utiliser la fonction defined
:
if(defined($x))...
Ce test est vrai si
$x
est définie, c'est-à-dire si elle ne vaut pas
undef
. Une erreur classique est d'écrire :
*incorrect* if($x!=undef) *incorrect*
Ne surtout pas
tenter de comparer une variable à undef
, car cela ne
fait pas ce qu'on attend.
La valeur undef
est une valeur fausse pour les
tests. Le test if( $x ) ...
est faux si
$x
est non-définie. Mais comme on le verra plus
tard, il est également faux si $x
vaut 0 (zéro) ou
bien la chaîne vide. Donc un test if( $x ) ...
est
potentiellement dangereux. Pour tester si une variable est définie, une
seule bonne façon : if(defined($x))...
Sur les nombres, les opérateurs classiques sont disponibles : +
- / * %
; ce dernier opérateur %
est le
modulo, c'est-à-dire le reste de la division entière du premier opérande
par le second. Notez que la division effectuée par l'opérateur
/
n'est pas une division entière mais une division
réelle, cela même si ses opérandes sont entiers (2/3 vaut 0.6666...) ; si
vous voulez effectuer une division entière, il vous faut tronquer le
résultat de la division précédente avec int()
:
l'expression int($x/$y)
vaut le quotient de la
division entière de $x
par $y
.
Des raccourcis existent : += -= *= /= %=
.
Ces opérateurs sont à la fois une opération arithmétique et une
affectation : $x+=3
est équivalent à
$x=$x+3
mais en plus synthétique : on ajoute 3
à $x
. L'instruction $y*=5
multiplie $y
par 5.
Il existe aussi des auto-incrémenteurs et des auto-décrémenteurs : ++
et --
qui peuvent être placés avant ou après une
variable : ils ajoutent ou déduisent 1 à cette variable.
$x++
à le même effet que $x+=1
ou que $x=$x+1
.
L'opérateur **
correspond à la puissance :
2**10
vaut 1024.
Les fonctions suivantes manipulent les nombres :
sin($x) cos($x)
renvoient le sinus et le cosinus
de $x
.
exp($x) log($x)
renvoient
e puissance $x
et le logarithme en base e de $x
.
abs($x)
renvoie la valeur absolue
de $x
.
sqrt($x)
renvoie la racine carrée
de $x
.
Voici quelques règles de conversion en contexte numérique.
Les chaînes de caractères représentant exactement un nombre sont
converties sans problème ; "30" + "12"
vaut 42.
Dans tous les autres cas (énumérés dans ce qui suit), l'option
-w
provoquera un message d'avertissement.
Les scalaires commençant par un nombre sont converties en ce nombre :
"34.2blabla"
vaudra 34,2.
Les autres valeurs scalaires (y compris undef
)
sont converties en 0.
Conclusion : utilisez toujours l'option -w
!
Les chaînes de caractères ont aussi leurs opérateurs. Le point
(.
) permet de concaténer deux chaînes :
l'instruction $x="bon"."jour"
a pour effet d'affecter la chaîne "bonjour" à $x
(pas de gestion de la mémoire à effectuer).
Cet opérateur est, entre autres cas, utile lorsque certaines
parties de la chaîne sont les valeurs de retour de fonctions ;
en effet, il suffit souvent d'utiliser les substitutions
effectuées dans les chaînes délimitées par des doubles quotes
pour concaténer deux chaînes.
L'opérateur x
est la multiplication pour les chaînes
de caractères : "bon"x3
vaut
"bonbonbon"
. Fort sympathique ...
Les raccourcis suivant peuvent être utilisés : .= x=
L'expression $x.=$y
est équivalente à
$x=$x.$y
et concatène donc $y
à la fin de $x
.
Voici un certain nombre de fonctions utiles qui manipulent les chaînes de caractères :
length($x)
renvoie la longueur de la chaîne
$x
. Par exemple
length("bonjour\n")
vaut 8
et length('bonjour\n')
vaut 9.
chop($x)
supprime le dernier caractère de la
chaîne $x
(la variable $x
est modifiée).
Ce caractère est renvoyé par la fonction : $c =
chop($l);
chomp($x)
supprime le dernier caractère de
$x
s'il s'agit d'une fin de ligne
(la variable $x
est modifiée). Cette fonction peut prendre
plusieurs arguments, chacun subira un sort similaire. Ne pas écrire
*incorrect* $x=chomp($x) *incorrect*
car
chomp
renvoie le nombre de caractères supprimés.
Cette fonction nous sera très utile lorsque nous lirons des fichiers
ligne à ligne.
reverse($x)
en contexte scalaire, renvoie la
chaîne composée des caractères de $x
dans l'ordre
inverse. Par exemple $v = reverse("bonjour\n")
affecte "\nruojnob"
à $v
.
On rencontrera aussi cette fonction chez les listes (son comportement
dépend du contexte).
substr($x,
offset,
length)
vaut la sous-chaîne de position offset et de longueur length.
Les positions commencent à 0 :
substr("bonjour",1,2)
vaut on
.
La longueur peut être omise, dans ce cas toute la partie droite de la
chaîne est sélectionnée.
Cette fonction peut être une lvalue, c'est-à-dire qu'on peut lui affecter une valeur (lvalue pour left-value : à la gauche du signe égal de l'affectation) :
my $v = "salut toi"; substr($v,5,1) = "ation à ";
$v
vaut alors "salutation à
toi"
. C'est là que l'on se rend compte que Perl gère
vraiment la mémoire tout seul !
index($chaîne,$sousChaîne,$position)
renvoie la
position de la première occurrence de $sousChaîne
dans $chaîne
. Le troisième paramètre,
s'il est fourni, indique la position du début de la recherche ; sinon
la recherche part du début de la chaîne (position 0).
rindex($chaîne,$sousChaîne,$position)
effectue la même recherche que la fonction index
mais en partant de la fin de la chaîne (la recherche est effectuée
de droite à gauche).
En contexte de chaîne de caractères, undef
vaut la chaîne vide ; l'option -w
provoquera
un message d'avertissement. Dans ce contexte, un nombre vaut la
chaîne de sa représentation décimale.
Les booléens (type de données ayant pour seules valeurs vrai et faux) n'existent pas en tant que tels en Perl, on utilise les scalaires pour effectuer les test (comme C le fait avec les entiers). Il me faut donc préciser quelles sont les valeurs scalaires vraies et quelles sont les fausses.
Les valeurs fausses sont :
0, c'est-à-dire l'entier valant zéro,
"0"
ou '0'
, c'est-à-dire la
chaîne de caractères ne comportant que le caractère zéro (pas le caractère
\0
de code ASCII zéro, mais 0
de code 48),
la chaîne vide :""
ou ''
(ce qui est la même chose),
undef
Toutes les autres valeurs sont vraies, par exemple : 1, -4.2,
"blabla"
etc. La plus originale est "00"
qui vaut l'entier 0 dans les opérations numériques, mais qui est vraie ...
Il existe deux catégories d'opérateurs de test : ceux pour lesquels on
impose un contexte numérique aux opérandes et ceux pour lesquels on
impose un contexte de chaîne de caractères. Par exemple
==
teste l'égalité de deux nombres (contexte
numérique) et eq
teste l'égalité de deux chaînes
(contexte de chaîne). ("02"=="2")
est vrai alors que
("02" eq "2")
est faux. La différence est encore
plus flagrante pour les opérateurs d'infériorité et de supériorité ;
<
teste l'ordre entre nombres,
lt
teste l'ordre ASCII entre chaînes ; donc
(9<12)
est vrai alors que
(9 lt 12)
est faux car 9 est après 1 dans la table ASCII. Confondre
ou mélanger ces deux types d'opérateurs est une erreur très
courante que font les débutants, ainsi que les initiés qui ne font pas
attention ...
Sachez que l'option -w
permet souvent de repérer
ces situations.
Voici un tableau décrivant les opérateurs de tests :
contexte imposé | numérique | de chaînes |
égalité | == |
eq |
différence | != |
ne |
infériorité | < |
lt |
supériorité | > |
gt |
inf ou égal | <= |
le |
sup ou égal | >= |
ge |
comparaison | <=> |
cmp |
Les opérateurs booléens classiques sont présents :
expr1&&
expr2 est vrai si expr1
et expr2 sont vraies (si expr1 est faux expr2 n'est pas évaluée),
expr1||
expr2 est vrai si expr1
ou expr2 est vraie (si expr1 est vrai expr2 n'est pas évaluée),
!
expr est vrai si expr est fausse.
Il existe aussi les opérateurs opérateurs and or
et
not
. Ceux-ci ont la même table de vérité que les précédents,
mais sont d'une priorité plus faible.
Les deux opérateurs cités à la dernière ligne du tableau ne sont pas des
opérateurs de test mais des opérateurs de comparaison ; ils sont présents
dans ce tableau en raison des similitudes qu'ils ont avec les opérateurs
de test en ce qui concerne le contexte imposé aux opérandes. Ces
opérateurs renvoient un nombre qui dépend de l'ordre entre leurs deux
paramètres. L'expression ($x<=>$y)
est :
positive si $x
est un nombre plus grand que
$y
,
négative si $x
est un nombre plus petit que
$y
,
nulle si $x
et $y
sont des
nombres égaux.
Cet opérateur <=>
est surnommé
spaceship (vaisseau spatial en français)
en raison de sa forme ;-) ...
Pour l'opérateur cmp
, la comparaison se fait sur
l'ordre des chaînes selon la table ASCII. Ces opérateurs seront fort
utiles lorsque nous parlerons de la fonction sort
qui effectue le tri des listes.
Ici nous allons apprendre à contrôler le flux des instructions en Perl. En effet un programme n'est pas qu'une simple suite d'instructions se déroulant linéairement une fois et une seule.
Il faut savoir que Perl (tout comme le C) permet d'indenter notre code comme bon nous semble, les exemples qui suivent comportent donc des choix personnels d'indentation qui peuvent diverger des vôtres.
Ces instructions permettent de conditionner l'exécution d'instructions
à la valeur de vérité d'une expression.
L'instruction la plus usitée est le if
(si en français)
qui a besoin d'une expression et d'un bloc d'instructions.
Cette expression sera évaluée en contexte scalaire
et servira de condition ; si elle est vérifiée, le bloc d'instructions
sera exécuté.
if( $x != 1 ) { print "$x\n"; }
Ce code Perl a pour effet d'afficher la variable $x
si elle ne vaut pas 1. Plusieurs instructions peuvent être placées dans
le bloc, elles seront alors toutes exécutées si la condition est vraie.
Notez que les accolades ({}
) sont
obligatoires pour délimiter le bloc (contrairement au C).
Il est possible d'exécuter d'autres instructions dans le cas où la
condition est fausse. On utilise pour cela l'opérateur else
(sinon en français)
qui, lui aussi, est suivi d'un bloc d'instructions :
if( $x == $y ) { print "\$x et \$y sont égaux\n"; } else { print "\$x et \$y sont différents\n"; }
Le fait que les accolades sont obligatoires a pour conséquence que le programme suivant est incorrect :
if( condition1 ) { instructions1 } else # Attention ce code est incorrect if { instructions2 }
Il faut en effet entourer le second if
par
des accolades comme ceci :
if( condition1 ) { instructions1 } else { if( condition2 ) { instructions2 } }
Si le programmeur se sent des envies de devenir sylviculteur
en plantant des forêts d'ifs, il faudrait donc qu'il utilise
de multiples couples d'accolades. Pour ne pas rencontrer les mêmes
problèmes que le Lisp en rencontre pour les parenthèses ;-),
Perl met à notre disposition l'instruction
elsif
qui permet de cumuler le comportement
d'un else
et d'un if
tout en faisant l'économie d'un couple d'accolades :
if( condition1 ) { instructions1 } elsif( condition2 ) { instructions2 } else { instructions3 }
L'instruction switch
de C n'a pas
d'équivalent direct en Perl ; il faut pour cela planter
une forêt d'ifs, comme dans l'exemple précédent.
Mais Perl n'en reste pas là. Il existe une syntaxe très utilisée pour effectuer une unique instruction si une condition est vérifiée :
instruction if( condition );
On parle ici de modificateur d'instruction.
Pour cette syntaxe, les parenthèses sont optionnelles autour de la
condition, mais je vous conseille de les mettre systématiquement pour
une meilleure lisibilité.
Le code suivant affiche la variable $s
si elle est définie :
print "$s\n" if( defined($s) );
On notera que cette syntaxe ne permet pas l'usage d'un
else
.
L'instruction unless
a exactement
le même rôle que le if
, à la différence
que les instructions seront effectuées si la condition
est fausse (il est aussi moins dépaysant d'en faire des forêts).
unless( expression )
est équivalent à
if( !(expression) )
dans toutes les constructions
précédemment citées.
Les boucles permettent d'exécuter plusieurs fois les mêmes instructions
sans avoir à écrire plusieurs fois les mêmes lignes de code.
Bien souvent nous avons besoin de modifier une variable à chaque étape ;
dans ce cas nous utiliserons l'instruction for
(pour en français) dont voici la syntaxe :
for( initialisation; condition; incrément ) { instructions; }
La boucle for
prend trois expressions entre
parenthèses : la première expression permet d'initialiser la
variable de boucle, la deuxième est une condition de continuation
et la dernière permet de modifier la valeur de la variable de boucle.
Quand la boucle démarre, la variable est initialisée (expression 1) et le test est effectué (expression 2). Si cette condition est vérifiée, le bloc d'instructions est exécuté. Quand le bloc se termine, la variable est modifiée (expression 3) et le test est de nouveau effectué (expression 2). Si la condition est vérifiée, le bloc d'instructions est ré-exécuté avec la nouvelle valeur pour la variable de boucle.
Tant que le test reste vrai, le bloc d'instructions et l'expression de modification de la variable sont exécutés. À l'arrêt de la boucle, les instructions qui suivent la boucle sont exécutées.
L'exemple suivant affiche tous les entiers pairs de 0 à 20 inclus :
for( my $i=0; $i<=20; $i+=2 ) { print "$i\n"; }
La boucle s'arrête lorsque $i
vaut 22.
Cette variable est déclarée dans le bloc d'initialisation
et n'existe donc que dans la boucle. Notez qu'il est tout à
fait possible d'utiliser une variable pré-existante comme
variable de boucle (et donc de ne pas faire de my
dans la partie initialisation) ; dans ce cas, après exécution
de la boucle, la variable vaut la dernière valeur qui lui a été
affectée au cours de la boucle.
Une autre boucle existe : la boucle while
(tant que en français)
dont voici la syntaxe :
while( condition ) { instructions; }
Les instructions sont effectuées tant que la condition est vraie. La partie initialisation doit avoir été effectuée avant la boucle ; la partie modification de la variable doit avoir lieu dans le bloc d'instructions.
L'exemple suivant affiche lui aussi les entiers pairs de 0 à 20 :
my $i = 0; while( $i <= 20 ) { print "$i\n"; $i+=2; }
La seule différence entre les deux exemples est le fait que,
dans le cas du while
, la variable
$i
existe après la boucle.
Comme pour le if
, certaines facilités sont
offertes pour le while
. Tout d'abord,
la syntaxe suivante est correcte :
instruction while( condition );
Elle permet d'exécuter plusieurs fois une instruction et une seule tant qu'une condition est vérifiée.
Ensuite, il existe une instruction until
(jusqu'à en français) qui a la même syntaxe
que le while
mais qui demande une condition d'arrêt (comme unless
pour if
) : until(condition)
est équivalent à while(!(condition))
Lors de l'exécution d'une boucle, il est fréquent de rencontrer
des cas particuliers que l'on souhaiterait sauter ou pour lesquels
on aimerait mettre fin à la boucle. Les instructions next
,
last
et redo
vont nous servir à cela
dans les boucles for
, while
ou until
.
L'instructions next
(suivant en
français) provoque la fin de l'exécution du bloc, le programme évalue
directement l'incrément (dans le cas d'une boucle for
) puis le test
est effectué.
L'instructions last
(dernier en
français) provoque la fin de la boucle, ni l'incrément ni le test
ne sont effectués.
L'instructions redo
(refaire en
français) provoque le redémarrage du bloc d'instructions sans que la
condition ni l'incrémentation ne soient effectuées.
L'exemple suivant est une autre façon d'imprimer à l'écran les entiers pairs de 0 à 20 :
my $i = -1; while( 1 ) # 1 est vrai { $i++; last if( $i > 20 ); next if( $i%2 != 0 ); print "$i\n"; }
Dans le cas où l'on souhaite exécuter le bloc d'instruction une fois avant d'effectuer le test, on peut utiliser la syntaxe suivante :
do { instruction; } while( condition );
Pour des raisons trop longues à exposer ici, il ne faut pas utiliser
les instructions next
, last
et redo
dans le cas de la boucle do while
.
Nous verrons dans les articles suivants qu'il existe une autre structure
de boucle : foreach
. Elle permet d'itérer sur les
éléments d'une liste (notion que nous aborderons à cette occasion). Vous
verrez alors qu'en Perl on utilise beaucoup le foreach
et assez peu le for(;;)
.
Voici un petit exemple de programme Perl ; il n'est pas très utile dans la vie de tous les jours, mais il utilise beaucoup des notions abordées dans cet article. Si vous parvenez à comprendre tout ce qu'il fait, vous n'aurez pas perdu votre temps à le lire !
1: #!/usr/bin/perl -w 2: use strict; 3: my $v ="##########"; 4: for( my $i=9; $i>0; $i-- ) 5: { 6: print("$i impair\n") 7: if( $i % 2 ); 8: print( "-"x$i . "\n") 9: unless( $i % 3 ); 10: substr( $v, $i, 0 ) = $i; 11: } 12: print("$v\n");
Voici le détail du rôle des lignes :
1: Le shebang Perl avec l'option -w
(affichage de messages d'avertissement)
2: Cette instruction rend le langage moins permissif, je vous conseille de toujours la placer au début de vos programmes.
3: Nous déclarons et initialisons une variable scalaire.
4: Une boucle for
. Déclaration et initialisation
à 9 de la variable de boucle $i
. Nous continuerons
tant qu'elle est strictement positive ; à chaque étape nous la
décrémenterons : elle variera donc de 9 à 1.
5 et 11: accolades délimitant le bloc de la boucle.
6 et 7: affichage d'une chaîne dans le cas où $i
est impair.
8 et 9: affichage d'une chaîne dans le cas où $i
est multiple de 3. Cette chaîne comporte $i
caractères
moins (-
).
10: insertion de $i
dans la chaîne
$v
en position $i
(une longueur de 0 provoque une insertion et non un remplacement).
12: Affichage de la variable $v
L'affichage suivant est donc effectué :
9 impair --------- 7 impair ------ 5 impair 3 impair --- 1 impair #1#2#3#4#5#6#7#8#9#
Après ce tour d'horizon des concepts de bases de Perl, vous voilà désormais bien armé pour affronter la suite. Vous avez maintenant les réflexes indispensables à une bonne compréhension de notions plus évoluées.
Dans le prochain numéro, nous aborderons d'autres types de données fort utiles et, vous le verrez, très intuitifs à manipuler : les tableaux, les listes et les tables de hachages ; nous écrirons aussi nos premières fonctions. Nous commencerons alors à entrevoir la puissance de Perl.
N'hésitez pas à m'écrire pour toute remarque, suggestion ...