Et voici la second Q/R:
_____________
Mais, alors, à quoi servent les listes?
A côté des différences importantes vues dans la question précédente, il y a de nombreux cas où une liste et un tableau se comportent de la même façon et un bon nombre des fonctions (sort, map, grep, join, for, foreach, etc.) sont utilisables de la même manière ou presque sur des listes et des tableaux. Seules les fonctions qui modifient un tableau ne sont généralement pas utilisables sur une liste, sauf éventuellement à créer une autre liste.
En fait, de nombreux opérateurs ou instructions de Perl travaillent non sur des tableaux, comme on peut le croire, mais sur des listes. Si vous passez un (ou plusieurs) tableau(x) en paramètre à une fonction, la fonction reçoit en fait une (seule) liste d'alias vers les éléments du (ou des) tableaux, et cette liste d'alias est mise dans le tableau @_. En particulier, s'il y avait plusieurs tableaux en entrée, la fonction n'a aucun moyen de distinguer les différents tableaux, elle ne "voit" qu'une seule grande liste indifférencié. Il en va de même en sens inverse quand une fonction renvoie plusieurs tableaux: l'instruction appelante ne reçoit en retour qu'une seule liste indifférenciée, et c'est à elle de la stocker éventuellement dans un tableau (ou d'autres variables) si les éléments de la liste doivent encore être manipulés.
Il a été vu lors d'une question précédente que l'on peut accéder aux éléments individuels d'une liste en utilisant un indice entre crochets, exactement comme avec un tableau:
my $second = (1, 2, 3, 4)[1]; #second vaut maintenant 2
Cela ne présente pas beaucoup d'intérêt avec une liste codée en dur comme ci-dessus (on pourrait aussi bien écrire: my $second = 2; ), mais cette possibilité peut être très utile avec des listes générées par une instruction du programme. Par exemple, la fonction split divise une chaîne de caractère en une liste de sous-chaînes (en utilisant le premier argument comme séparateur). Souvent, on affecte la liste de sous-chaînes produite à un tableau:
my @mots = split / /, "La contrepèterie est l'art de décaler les sons.";
Le split génère une liste de huit sous-chaînes que l'on affecte ensuite au tableau @mots. Il arrive toutefois assez souvent, dans ce genre d'utilisation de split, que l'on ait besoin de seulement une ou deux des sous-chaînes générées par l'opérateur. On peut alors prélever une tranche du tableau @mots:
my ($mot2, $mot5) = @mots[1, 4]; # le "sigil" à employer ici est bien @ (et non $) parce que l'on récupère non pas un scalaire, mais une collection de deux scalaires
Mais on peut aussi se passer de créer un tableau intermédiaire et récupérer les mots recherchés directement en une seule instruction plus concise:
my ($mot2, $mot5) = (split / /, "La contrepèterie est l'art de décaler les sons.")[1, 4];
De même, on peut parcourir séquentiellement les valeurs d'une liste avec les opérateurs de boucle for ou foreach. Par exemple, l'instruction suivante affiche la table de multiplication par 5:
print $_, " * 5 = ", $_ * 5, "\n" for 1..10;
ce qui imprime:
1 2 3 4 5 6 7 8 9 10
| 1 * 5 = 5
2 * 5 = 10
3 * 5 = 15
4 * 5 = 20
5 * 5 = 25
6 * 5 = 30
7 * 5 = 35
8 * 5 = 40
9 * 5 = 45
10 * 5 = 50 |
Ou, si l'on désire un affichage plus "propre":
printf "%2d%s%2d%s", $_, " x 5 = ", $_ * 5, "\n" for 1..10;
Ce qui donne:
1 2 3 4 5 6 7 8 9 10
| 1 x 5 = 5
2 x 5 = 10
3 x 5 = 15
4 x 5 = 20
5 x 5 = 25
6 x 5 = 30
7 x 5 = 35
8 x 5 = 40
9 x 5 = 45
10 x 5 = 50 |
Cette syntaxe permet aussi de répéter un certain nombre de fois une instruction simple. Supposons par exemple que nous voulions lire un ficher de données en sautant préalablement les six premières lignes d'en-tête. Nous pouvons le faire ainsi:
1 2 3 4 5
| open my $FH, "<", $file_in or die "Impossible d'ouvrir le fichier $file_in $!";
<$fh> for 1..6; # on jette les six premières lignes
while (my $line = <$fh>) {
# traitement des lignes de données ...
} |
De même, si nous avons besoin de lire les éléments d'un tableau en ayant connaissance de leur indice, au lieu de la boucle for de type C:
1 2 3 4
| my @mois = qw /undef jan feb mar apr may jun jul aug sep oct nov dec/; # première valeur à undef pour que les mois aillent de 1 à 12 (et non 0 à 11)
for (my $i = 1; $i ++; $i <= 12) {
print "Le mois numéro $i de l'année est $mois[$i]\n";
} |
nous pouvons utiliser une simple liste pour gérer l'indice $i:
1 2 3
| for my $i (1..12) {
print "Le mois numéro $i de l'année est $mois[$i]\n";
} |
ou de façon encore plus concise:
print "Le mois numéro $_ de l'année est $mois[$_]\n" for (1..12);
Un dernier point qui ne sera que très brièvement esquissé ici: beaucoup des opérateurs de listes de Perl reçoivent en entrée et/ou produisent en sortie une liste. Il est possible d'utiliser en entrée d'un opérateur la liste produite en sortie par un autre opérateur. Ainsi, par exemple, l'instruction suivante enchaîne quatre opérateurs de listes, split, sort, foreach et print:
print $_, "\n" foreach sort split / /, "la contrepèterie est l'art de décaler les sons.";
Ce qui imprime les mots de la phrase triés par ordre lexicographique:
1 2 3 4 5 6 7 8
| contrepèterie
de
décaler
est
l'art
la
les
sons. |
Une telle suite d'instructions se lit de droite à gauche: la fonction split commence par découper la phrase en entrée en mots individuels et passe cette liste de mots à la fonction sort; cette dernière trie la liste de mots reçus de split et passe la liste triée à l'opérateur foreach, lequel passe ensuite le mots triés un par un à print, qui imprime les mots triés assortis d'un retour à la ligne. On voit qu'il se forme un pipeline dans lequel les données en entrée subissent des transformations successives. Le lecteur intéressé trouvera des informations beaucoup plus détaillées sur ce genre de constructions très puissantes dans ce tutoriel: La programmation fonctionnelle en Perl - Partie 1: les opérateurs de liste.
Partager