====== Généralités ======
====== 1. Les variables ======
===== Nommage =====
Les noms de variables suivent les conventions du C :
* elles doivent commencer par une lettre
* elles peuvent contenir des lettres, des chiffres et le caractère **_** (souligné)
* la distinction majuscule/minuscule est effective
Dans le style de programmation Perl traditionnel, on utilise les minuscules pour les noms de variables. Une variable commence par un caractère qui détermine son type, et qui ne peut qu'être **$** (dollar), **@** (arobase) ou **%** (pourcent) :
$scalaire = "bonjour";
@liste = ("bonjour", 1, "monde");
%hash = ("message" => "bonjour");
En Perl, on peut utiliser le même nom de variable pour des types de différents de manière indépendante. Par exemple, ces 3 variables co-existent et sont indépendantes :
$toto = "bonjour";
@toto = ("bonjour", 1, "monde");
%toto = ("message" => "bonjour");
**Note**: il existe quelques exceptions notables, dont le descripteur de fichier qui n'a pas de préfixe particulier et utilise traditionnellement des majuscules (ex: open(HANDLE, "hello.txt")).
===== Utilisation =====
Les variables en Perl n'ont pas besoin d'être déclarées et son globales par défaut. Comme leur type fait partie de leur nom (via le symbole préfixe ad hoc), ceci est justifiable :
for ($i = 0; $i < 5; $i++) { ... }
Cependant, en pratique et notamment lorsqu'on introduira les fonctions et les modules, nous verrons qu'il s'agit d'une pratique peu fiable et à proscrire. On va alors prendre l'habitude de déclarer nos variables :
use strict;
my $i;
for ($i = 0; $i < 5; $++) ...
**Note**: la directive **use strict** dans le dernier exemple indique au compilateur de Perl que nous voulons vérifier que toutes les variables utilisées sont bien déclarées au préalable, et générer une erreur le cas échéant.
===== Valeurs =====
Une variable peut ne pas avoir de valeur, elle est alors **indéfinie** (on pourrait dire non **initalisée** pour comparer au C). Cela veut dire que d'une part une "non-valeur" est représentable en Perl, mais en plus que nous pouvons mettre à contribution l'interpréteur pour détecter les variables qui sont utilisées sans être initialisées. C'est ce que l'on fait souvent en utilisant l'option **-w** de Perl :
#!/usr/bin/perl -w
my $message;
print "message: $message\n";
... va générer le //warning// suivant lors de l'exécution: //Use of uninitialized value in concatenation (.) or string at blob line 4.//. Voici une illustration plus complète du cycle de vie d'une variable :
# $toto n'existe pas (encore)
my $toto; # $toto est déclarée et peut être utilisée, mais n'est pas définie
print "non-valeur" if not defined $toto;
$toto = 42; # $toto est définie
$toto = undef; # $toto n'est plus définie
**Note**: on ne peut pas "dé-déclarer" une variable !
====== 2. Les scalaires ======
Une valeur scalaire peut représenter les types de base suivants :
* un nombre entier
* un nombre à virgule flottante
* une chaîne de caractère
* une référence (que nous verrons plus tard)
my $int = 42;
my $float = 42.53;
my $string = "Bonjour";
my $string2 = 'Bonjour';
Perl permet de passer d'une représentation à une autre de manière transparente. Le point délicat consiste à comprendre quand le changement de représentation a lieu: on parle alors de contexte d'une opération. Voici par exemple deux façons de considérer la valeur "0" :
my $val = 42;
print "la valeur numérique n'est pas nulle" if $val != 0;
print "la chaîne de caractère n'est pas vide" if $val ne //;
my $int = $val + 1; # $int vaut 43
my $string = $val . " le monde"; # $string vaut 'Bonjour le monde'
S'il est toujours possible de transformer une valeur entière en une chaîne de caractère (en utilisant sa représentation décimale, éventuellement en "écriture scientifique"), il n'est toujours évident d'interpréter une chaîne de caractère comme une valeur numérique: si la conversion échoue, Perl utilise la valeur numérique **0**.
print "-42" + 1; # Affiche -41
print "- 42" + 1; # Affiche 1
**Note**: certains opérateurs (comme la division) utilisent un contexte "virgule flottante". Il est possible de forcer globalement le contexte de la plupart des opérations numériques en contexte entier avec la directive //use integer//.
my $age = 24;
my $majeur = ($age >= 18) ? true : false;
$majeur prendra la valeur true car 24 supérieur à 18, dans le cas contraire la variable aurait pris la valeur false.
===== Opérateurs arithmétiques =====
Leur contexte est bien entendu entier, et tout scalaire impliqué dans une opération arithmétique est (éventuellement) converti en valeur numérique. On retrouve les opérateurs classiques :
$n = ($x / 2 + 5) * 3 - $y;
$n += 10;
$n++;
Pour comparer des valeurs arithmétiques, on utilise les opérateurs "naturels" :
my $a = 5;
my $b = 9;
print "différence" if $a != $b;
print "égalité" if $a ====== $b;
print "supériorité" if $a > $b;
===== Opérateurs de chaîne =====
Leur conversion transforme automatiquement toute valeur numérique impliquée en sa représentation décimale. **Note**: si vous voulez plus de contrôle sur cette conversion, il faut l'effectuer explicitement avec //sprintf// (par exemple).
$t = "Bonjour";
$t = $t . " le monde";
La plupart des manipulations intéressantes avec les chaînes se font via les expressions régulières. On trouve bien sûr les fonctions basiques comme //length//, //index// et //substr// par exemple.
Pour comparer des chaînes, on utilise les opérateurs **eq** et **ne** dédiés à cet effet :
my $u = "Paul";
my $v = "Pierre";
print "différence" if $u ne $v;
print "identité" if $u eq $v;
====== 3. Les listes ======
Cette structure de donnée permet à la fois de gérer des tableaux et des listes de façon simple. Les éléments d'une liste en Perl sont des scalaires :
my @liste = (1, "bonjour", "etc...");
Pour accéder à un élément de la liste, il existe plusieurs moyens :
my $a = shift(@liste); # $a vaut 1
my $b = $liste[0]; # $b vaut "bonjour"
my $c = $liste[-1]; # $c vaut "etc..."
my (undef, $d) = @liste; # $d vaut "etc..."
Pour obtenir la taille d'une liste :
my $size = @liste;
my $size = scalar(@liste);
my $size = $#liste + 1;
**Note**: nous reviendrons sur les conversions entre listes et scalaires plus tard.
===== Manipulations diverses =====
* Méthode alternative pour construire une liste :
@liste = qw/Hello world !\n/;
@liste = split(/ /, "Hello world !\n");
* Extraire une partie de la liste (voir //splice// également):
@extrait = @liste[1..2]; # world, !\n
* Concaténer les éléments d'une liste (dans un scalaire):
$phrase = join(' ', @mots);
* Ajouter un élément à une liste:
push @liste, 'bonjour';
* Retirer le premier élément de la liste:
$element = shift @liste;
* Retirer un élément quelconque:
delete $liste[2]; # Différent de $liste[2] = undef; !
* Trier une liste:
@direct = sort @liste;
@inverse = sort { ! $a <=> $b } @liste;
* Filtrer une liste:
@selection = grep { $_ > 10 } @liste;
* Transformer les éléments d'une liste un par un:
@resultat = map { $_ * 2 } @liste;
===== Parcours =====
Une des opérations les plus courantes en Perl consiste à parcourir les éléments d'une liste un par un. Il existe de nombreuses façons de le faire, dont les plus courantes sont :
foreach $mot (@hello) { print "$mot"; }
foreach (@hello) { print $_; }
foreach (@hello) { print; }
print foreach @hello;
Il est possible dans une structure de contrôle d'omettre le nom de l'itérateur (//$mot//), la variable //$_// est alors automatiquement utilisée. De même, la plupart des fonctions Perl vont spontanément utiliser cette variable comme argument si aucun ne lui est fourni (//print//).
A noter qu'avec les fonctions //grep// et //map//, on peut éviter de nombreuses boucles explicites et simplifier grandement ses programmes.
====== 4. Les tableaux associatifs (hashes) ======
Cette structure de donnée est appelée **ARRAY** ou **hash** dans la documentation Perl. En français, nous parlerions de tableaux associatifs ou de tables de hachage. Nous utiliserons l'anglicisme //hash// par la suite.
Exemple de déclaration et d'utilisation :
# Intialisation
my %menu = (
"entrée" => "salade",
"plat" => "nouilles",
"dessert" => "yaourt",
"prix" => 42
);
# Assignation
$menu{'prix'} = 45;
# Lecture
print "Plat: ".$menu{'plat'};
**Note**: on peut trouver des intialisations où le symbole **=>** est simplement une virgule, Perl pouvant effectivement considérer un tableau associatif sous sa forme "à plat", c'est-à-dire sous forme de liste de paires (clé, valeur) successives.
===== Manipulation diverses =====
* Obtenir la liste des clés (indices) :
my @cle = keys %menu;
* Vériier la présence d'une clé :
print "Le menu a un prix" if defined $menu{'prix'};
* Ajouter un élément quelconque :
$menu($info) = "blablabla";
* Retirer un élément quelconque :
delete $menu{'prix'}; # Différent de $menu{'prix'} = undef; !
===== Parcours =====
Les hashes Perl ne sont pas "ordonnés" (comme par exemple en PHP où l'ordre d'insertion est préservé), l'ordre de parcours des éléments n'est donc a priori pas défini - et en général différent à chaque parcours. Voici le parcours classique à l'aide de **each** :
while (($cle, $val) = each %menu) {
print "$cle: $val\n";
}
On peut également extraire d'abord la liste de clés, et ainsi éventuellement effectuer un tri pour obtenir un parcours ordonné selon les clés :
print "$_: $menu{$_}\n" foreach (sort keys %menu); # Plus lisible que sort(keys(%menu))
====== 5. Contextes liste et scalaire ======
De nombreuses fonctions et constructions en Perl peuvent accepter pour un même argument un scalaire ou une liste. Le point délicat vient du fait qu'en général, la même fonction ou construction se comporte différemment suivant qu'elle est en mode liste ou scalaire.
Par exemple, une liste évaluée dans un contexte scalaire renvoie le nombre de ses éléments. L'expression suivante vérifie donc bien que le nombre d'arguments fourni au programme est 2 (car d'une part, ====== est un opérateur arithmétique, d'autre part 2 est également un scalaire) :
if (@ARGV ====== 2) ... # Le nombre d'arguments de @ARGV est considéré
On peut changer le contexte simplement en comparant avec une liste, et cette-fois ce sont bien les éléments de //@ARGV// qui seront pris en comptes :
if (@ARGV ====== ('--output', 'png')) ... # Le contenu de @ARGV est considéré
**Note 1**: //print// sait automatiquement afficher une liste, mais il le fait en concaténant les éléments. On peut utiliser //print join("\n", @liste)// pour afficher un élément par ligne (etc).
**Note 2**: //print// ne sait pas nativement afficher un hash, mais il y a plein de solutions simples comme //print "$k: $v\n" while ($k, $v) = each %asso//.
Il existe d'autres contextes (comme celui du tableau associatif), mais ils sont utilisés de manière souvent moins transparente et sont clairement signalés.