Perl

De Ensiwiki
Aller à : navigation, rechercher

Introduction

Perl est un langage qui fait peur. Ses détracteurs le trouvent illisible et cryptique ; pourtant, il est très efficace et permet, quand on le connaît, d'écrire rapidement des scripts qui seraient beaucoup plus longs à mettre au point. En revanche, sa courbe d'apprentissage n'est pas spécialement rapide. Les mainteneurs du langage (dont Larry Wall, son créateur), considèrent qu'il faut privilégier les utilisateurs avancés plutôt que les novices, afin que Perl soit très efficace lorsqu'on le connaît.

Présentation du langage

Perl est né à une époque où ni sed, ni awk ne permettaient de réaliser des scripts un peu importants, il a donc incorporé de nombreuses fonctionnalités de ces deux outils.

Perl est-il installé sur mon système ?

Réponse courte : oui.

Réponse longue : la plupart des distributions de linux et de nombreux unices (dont Mac OSX) viennent avec Perl déjà installé. Au pire, il suffit d'installer les paquetages perl et perldoc (ou des noms similaires...). Sous Windows, on peut installer ActivePerl.

Hello world

Traditionnellement, on peut commencer par un programme qui affiche "Hello, world !" sur le terminal :

print "Hello, world !\n";

"\n", comme en C, correspond au caractère de passage à la ligne.

Pour lancer ce script il suffit, dans un terminal, d'exécuter perl script.pl. Si tout se passe bien, il nous dit coucou :

 $ perl script.pl
 Hello, world !
 $ 
Remarque pour utilisateur avancéOn peut aussi rendre le script exécutable en ajoutant la ligne #!/usr/bin/perl au tout début et en faisant chmod +x script.pl. Pour le lancer il suffit alors de faire ./script.pl.


Syntaxe

Au niveau de la structure, Perl ressemble beaucoup à C : c'est un langage à blocs, et il partage certaines structures de contrôle. En revanche, il est faiblement typé, c'est-à-dire qu'une même variable peut contenir un nombre, une chaîne de caractères ou une expression régulière par exemple.

Les commentaires commencent par un # et finissent jusqu'à la fin de la ligne. Les blancs (espaces, tabulations, retours à la ligne) sont ignorés.

Variables et types

Perl distingue trois types de données :

  • les scalaires
  • les tableaux
  • les tables de hachages (ou "hashtables", ou "hashes")

Les scalaires correspondent aux données simples : nombres (entiers ou flottants), caractères, mais aussi chaîne de caractères, expressions régulières et références (on y reviendra plus tard !)

Les tableaux sont une liste ordonnée de scalaires, indexés par des entiers.

Les hashes sont des tableaux associatifs, associant une valeur scalaire à une clé (chaîne de caractère ou entier).

Pour distinguer ces types de variables, Perl utilise la notion de sigils (sigles). Ainsi, on préfixe les variables scalaires d'un $, les tableaux d'un @ et les hashes d'un %.

$a = 5;                      # un $calaire
@b = (4, 8, 15, 16, 23, 42); # un tableau (@rray)
%c = (rouge => 4,            # un hash (je ne sais pas pourquoi '%'...)
       vert => 7,
       bleu => 8);

print $a;         # affiche 5
print $b[3];      # affiche 16
print $c{'vert'}; # affiche 7

On remarque un point important : le sigil dépend du résultat, pas de la variable : l'élément en position 3 dans le tableau @b s'écrit $b[3], pas @b[3].


Nombres

Perl permet d'utiliser entiers et flottants. On dispose des opérateurs de comparaison suivants : <, >, <=, >=, ==, != et <=>. Les premiers font ce qu'on imagine !

<=> est l'opérateur vaisseau spatial. $a <=> $b vaut :

  • 1 si $a > $b
  • 0 si $a == $b
  • -1 si $a < $b

On retrouve bien sûr les opérateurs arithmétiques + - * / % (modulo) ** (exponentiation). Les opérateurs bit-à-bit de C sont aussi de la partie : & | ~ et ^.

Booléens et opérateurs logiques

Il n'y a pas de booléens en Perl. Comme en C, un booléen est une valeur numérique non nulle. Il existe plusieurs opérateurs chargés de manipuler les "booléens" :

  • && est le "et" logique, comme en C.
  • || est le "ou" logique, idem.
  • On retrouve également le !, le non logique.

L'évaluation paresseuse est appliquée : dans exp1 && exp2, exp2 n'est évaluée que si exp1 est vraie (cela permet de faire des constructions comme if($n!=0 && $a/$n > 0.5).

Il existe aussi des alias pour ces opérateurs : on peut écrire and, or et not. Ils font la même chose, mais leur priorité est beaucoup plus faible.

Il y a aussi xor, le ou exclusif logique (mais on ne peut pas l'écrire ^^)

Bien sûr, l'opérateur ternaire ? : est dans la place.

Variables non définies

Une variable peut ne pas avoir de valeur, sa "valeur" vaut alors undef. undef est aussi une fonction de perl qui sert à dé-définir une variable. On peut tester si une variable est définie avec la fonction defined.

Chaînes de caractères

On peut écrire les chaînes de caractères de plusieurs manières : avec ou sans interpolation.

Avec interpolation, un certain nombre de constructions dans la chaîne seront remplacées : les caractères échappés comme \n, mais aussi les variables. Sans interpolation, seul \' est remplacé par ' :

$jour = 4;
print "Nous sommes le $jour\n"; # Avec interpolation
                                # Affiche : Nous sommes le 4

print 'Nous sommes le $jour\n'; # Sans interpolation
                                # Affiche : Nous sommes le $jour\n

Il existe des opérateurs de comparaisons lexicographiques :

  • lt (less than)
  • gt (greater than)
  • le (less or equal)
  • ge (greater or equal)
  • eq (equal)
  • ne (not equal)
  • cmp (l'alter ego lexicographique de <=>)

Perl : les deux dialectes

Perl en tant que tel est extrêmement permissif : par exemple, on peut utiliser des variables non déclarées, un identifiant inconnu est interprété comme une chaîne ("print coucou" affiche "coucou"...). Il vaut mieux savoir ce qu'on fait, c'est pour ça qu'il existe un mode "strict".

Pour l'activer, ainsi que les avertissements (variables non initialisées, etc), c'est un bon réflexe de toujours commencer ses scripts par :

use strict;
use warnings;

Dans les premiers exemples, on ne déclarait pas les variables. La syntaxe pour déclarer une variable est :

my $a;        # variable non initialisée
my $b = 98;   # avec initialisation
my @k;        # tableau vide
my ($c, $d);  # déclaration multiple

En fait, les variables ainsi déclarées sont des variables lexicales : leur portée est limitée au bloc courant, et le masquage se fait comme en C.

Structures de contrôle

if

Le "if", simple et efficace. Bien entendu, les blocs elsif et else sont optionnels !

if (condition1) {
    ...lol...
} elsif (condition2) {
    ...blabla...
} elsif (condition3) {
    ...kikoo...
} else {
    ...rofl...
}

for

C'est la même boucle for qu'en C.

for($i=0;$i<5;$i++) {
    ...blabla...
}

while

C'est la même boucle while qu'en C.

while(condition) {
    ...blabla...
}

do...while

C'est la même boucle do/while qu'en C.

do {
    ...blabla...
} while(condition);

foreach

foreach permet de faire itérer une valeur sur un tableau :

foreach $variable (@tableau) {
    ...blabla...
}

L'intérieur du bloc est exécuté une fois par élément du tableau, en remplaçant à chaque fois $variable par un élément de @tableau.

L'instant où tout bascule

En pratique, personne n'utilise la forme ci-dessus pour foreach, on l'écrit plutôt :

foreach (@tab) {
    ...blabla...
}

Mais alors, où va la valeur d'itération ? La réponse est simple : dans une variable par défaut, nommée $_. (au cas où, je préfère préciser : pas besoin de la déclarer, $_ est déjà locale à chaque bloc.)

En fait, il y a de nombreuses constructions où Perl utilise $_ ou d'autres variables (comme @_) par défaut.

Routines, fonctions, astuces, bonheur

On peut déclarer une routine en utilisant sub :

sub fact {
    if ($_[0] <= 1) {
        return 1;
    }
    return $_[0] * &fact($_[0]-1);
}

print &fact(5);

Plusieurs remarques :

  • les routines n'ont pas d'arguments nommés ! en fait, les arguments sont passés par référence dans @_. Il y a plusieurs techniques pour contourner cette limitation.
  • HERE COMES A NEW SIGIL : & sert aux appels de fonction. En pratique, il est optionnel sauf si on définit une routine qui porte le même nom qu'une fonction interne de Perl.

Plusieurs astuces maintenant :

  • on peut prendre le premier argument de la fonction avec shift. En fait shift @tab retire et renvoie le premier élément de @tab. Quand on ne lui donne pas d'argument, il prend dans @_ (la magie de perl commence à arriver, non ?)
  • les opérateurs de contrôle ont une version postfixée, souvent plus lisible : action if(condition)

Ainsi, on peut réécrire la routine à la sauce Perl :

sub fact {
    my $n = shift;
    return 1 if ($n<=1);
    return $n * &fact($n-1);
}

Jouons un peu avec les tableaux

Les tableaux de Perl sont plutôt pratiques, puisqu'avec quelques fonctions, on peut s'en servir comme piles ou fifos.

shift

Prend le premier élément du tableau et le renvoie :

 avant   :  A | B | C | D
 après   :      B | C | D
 renvoie :  A

Par défaut, shift pioche dans @_.

unshift

Fait le contraire de shift : on lui donne un tableau et des éléments, et il ajoute ces éléments en tête, dans le même ordre.

 unshift @t, $x, $y
 
 @t avant :  A | B 
 @t après :  X | Y | A | B

pop

Prend le dernier élément et le renvoie.

 avant   :  A | B | C | D
 après   :  A | B | C
 renvoie :  D

push

Prend un liste d'éléments de l'ajoute à la fin du tableau.

 push @t, $x, $y
 
 @t avant :  A | B 
 @t après :  A | B | X | Y

reverse

Retourne un tableau.

Retour sur $@% - l'instant des révélations

En fait, les "types" de base de Perl sont essentiellement dans deux catégories : les scalaires et les agrégats. Première révélation donc : les hashes sont des tableaux déguisés, comme l'indique leur syntaxe :

my @tab = (3, 14, 15);
my %h   = (r => 43,
           g => 3,
           b => 21);

En fait, un hash est juste un tableau qui contient des données dans l'ordre clé, valeur, clé, valeur, etc. Et la "grosse flèche" => ? C'est en fait uniquement un raccourci qui veut dire "transforme moi en virgule et entoure ma partie gauche de guillemets". En fait, les hashes et les tableaux sont si liés qu'on peut assigner l'un à l'autre :

my @t = ('r', 43, 'g', 3, 'b', 21);
my %h = @t;
print $h{'r'};  # = 43 !
Remarque pour utilisateur avancéAttention avec le passage dans l'autre sens : lors de la "mise à plat" d'un hash, l'ordre n'est pas spécifié mais on obtiendra clé, valeur, clé, valeur, etc.
my %h = (r => 43, g => 3, b => 21);
my @t = %h;
# ici @t est soit ('r', 43, 'g', 3, 'b', 21),
#            soit ('r', 43, 'b', 21, 'g', 3),
#            soit ...


Une des fonctionnalités intéressantes de Perl est que les opérations se déroulent différemment selon qu'on soit en "contexte scalaire" ou en "contexte de liste" :

@t = (3, 14, 15, 92, 65);
@v = @t;                   # tableau en contexte de liste : copie de tableau
$n = @t;                   # tableau en contexte scalaire : $n contient le nombre d'éléments de @t, soit 5
($a, $b, $c, $d, $e) = @t; # tableau en contexte de liste : $a contient 3, $b contient 14, etc.
($f) = @t;                 # tableau en contexte de liste : $f contient 3.
$g = $b;                   # scalaire en contexte scalaire : $g contient $b.
@w = $a;                   # scalaire en contexte de liste : @w a un élément, $a.

En fait, de nombreuses fonctions renvoient une chose différente selon le contexte dans lequel elles sont appelées. Dans certains cas, c'est assez intuitif : par exemple keys %h renvoie, en contexte de liste, l'ensemble des clés du hash %h et en contexte scalaire, le nombre de ces clés.

En revanche, reverse ('kikoo', 'lol', 'rofl', 'mao', 'gcb'); renvoie un tableau à l'envers en contexte de liste (soit 'gcb', 'mao', 'rofl', 'lol', 'kikoo'), mais en contexte scalaire, renvoie une chaîne contenant l'ensemble des éléments du tableau, lue à l'envers (soit 'bcgoamlforlolookik'). Moralité : toujours lire la documentation.

On peut néanmoins "forcer le contexte". Par exemple, admettons qu'on veuille retourner 2 chaînes et les mettre dans un tableau. La première approche serait :

@x = ((reverse 'kikoo'), (reverse 'rofl'));

On s'attend à avoir en sortie ('ookik', 'lfor')... et pourtant ça ne fonctionne pas, car ici les 2 reverse sont évalués en contexte de liste : ils renvoient uniquement leur liste d'arguments (à 1 élément) à l'envers, c'est à dire la même... Pour forcer l'évaluation en contexte scalaire, on peut utiliser l'opérateur "scalar" :

@x = ((scalar reverse 'kikoo'), (scalar reverse 'rofl'));
# ('ookik', 'lfor')

Ainsi, pour obtenir le nombre d'éléments d'un tableau, on écrit souvent pour plus de lisibilité scalar @x. Il existe aussi un raccourci avec $# :

$len1 = scalar @k; # scalar ne sert à rien mais améliore la lisibilité
$len2 = $#k;       # forme alternative

Trouver plus de documentation

Les pages de man contiennent beaucoup d'informations réparties dans plusieurs entrées. Dans un terminal essayez notamment

 telesun> man perl
 telesun> man perlintro