Vous savez maintenant manipuler des tableaux et des listes, vous savez aussi écrire des fonctions : vous voila armés pour aborder les très puissantes et bien pratiques fonctions de gestion qui suivent.
La fonction join prend en paramètre un scalaire et une liste ; elle renvoie une chaîne de caractères comportant les éléments de la liste, concaténés et séparés par ce premier paramètre scalaire. Les arguments passés ne sont pas modifiés.
scalaire = join( séparateur, liste );
Voici quelques exemples :
$s = join(" ",1,2,3); La variable $s vaut alors la chaîne "1 2 3".
$s = join(',',$x,$y,$y); Les valeurs des trois variables sont jointes en les alternant avec des virgules. Le résultat est affecté à $s.
$s = join(" : ",@t); La variable vaut alors la concaténation des valeurs du tableau @t avec " : " pour séparateur.
La fonction split prend en paramètre un séparateur et une chaîne de caractères ; elle renvoie la liste des éléments de la chaîne de caractères délimités par le séparateur. Le séparateur est une expression régulière, notion que nous aborderons dans la suite, mais dont le minimum de connaissances suffit à cette fonction ; admettez ici qu'une telle expression est à placer entre slashs (/). Les arguments passés ne sont pas modifiés.
liste = split( /séparateur/, chaîne );
Voici quelques exemples :
@t = split(/-/,"4-12-455"); Le tableau comporte alors les éléments 4, 12 et 455.
($x,$y) = split(/==/,$v); Les deux variables auront pour valeur les deux premières chaînes de caractères qui soient séparées par deux signes d'égalité.
print join(':',split(/ /,'salut ici')); Affiche salut:ici (il existe des méthodes plus efficaces et plus lisibles de faire cela...).
La fonction sort prend en paramètre un bloc d'instructions optionnel et une liste ; elle renvoie une liste triée conformément au critère de tri constitué par le bloc d'instructions. La liste passée en argument n'est pas modifiée.
liste2 = sort( liste1 );
liste2 = sort( { comparaison } liste1 ); (attention à ne pas mettre de virgule entre le bloc d'instructions et la liste)
Tout au long du tri, le bloc d'instruction sera évalué pour comparer deux valeurs de la liste ; ces deux valeurs sont localement affectées aux variables spéciales $a et $b qui ne sont définies que dans le bloc et sur lesquels il faut donc effectuer la comparaison. Il faut faire particulièrement attention au fait que s'il existe des variables $a et $b dans le programme elles seront localement masquées par ces variables spéciales (source courante d'erreurs). Le bloc doit être composé d'une expression dont la valeur est :
C'est là qu'entrent en jeu les opérateurs de comparaison cmp et <=> : ils permettent de comparer respectivement les chaînes de caractères selon l'ordre lexical et les nombres selon l'ordre numérique. Si la fonction sort est appelée sans bloc d'instruction, la liste est triée selon l'ordre lexical.
Voici quelques exemples :
@s = sort( {$a cmp $b} @t ); La liste @s a pour valeur la liste @t triée selon l'ordre lexical.
@s = sort( @t ); Le fonctionnement est identique à l'exemple précédent.
@s = sort( {$a <=> $b} @t ); Le critère de tri est ici numérique.
@s = sort( {$a <=> $b or $a cmp $b} @t ); Une expression composée peut bien sûr servir de critère : le tri est ici d'abord numérique puis lexical. Cela permet de placer '8 navets' avant '12 carottes', et '12 carottes' avant '12 navets'.
@s = sort( { fonction($a,$b) } @t ); Vous pouvez écrire votre propre fonction de tri (à deux arguments) ; elle doit renvoyer un nombre dont la valeur dépend de l'ordre voulu (voir juste avant).
La fonction grep prend en paramètre un critère de sélection et une liste ; elle renvoie la liste des éléments correspondant au critère. La liste passée en argument n'est pas modifiée.
Le critère de sélection peut être soit une expression régulière (cas sur lequel nous reviendrons plus tard), soit un bloc d'instructions (cas sur lequel nous allons nous étendre) :
liste2 = grep { sélection } liste1; (attention : pas de parenthèses ni de virgule)
Les éléments renvoyés sont ceux pour lesquels l'évaluation du bloc d'instructions a pour valeur vrai. Durant cette évaluation, chacune des valeurs sera localement affectée à la variable spéciale $_ sur laquelle les tests devront donc être effectués.
Voici quelques exemples :
@t = grep { $_<0 } $x,$y,$z; Affecte à @t les éléments négatifs de la liste.
@s = grep { $_!=8 and $_!=4 } @t; Met dans @s les éléments de @t différents de 4 et de 8.
@s = grep { fonction($_) } @t; Vous pouvez écrire votre propre fonction de sélection ; elle doit renvoyer vrai ou faux selon si l'élément est à garder ou non.
En contexte scalaire, la fonction grep renvoie le nombre d'éléments qui correspondent au critère : $n = grep { .... } @t;
La syntaxe de grep comportant une expression régulière est la suivante :
liste2 = grep( /regexp/, liste1 );
En quelques mots, les éléments renvoyés seront ceux qui correspondront à l'expression régulière. Par exemple @s = grep( /^aa/, @t ); affecte à @s les éléments de @t qui commencent par deux lettres a. Plus d'explications sur les expressions régulières seront données dans la suite.
J'ai affirmé que la liste d'origine n'était pas modifiée, mais il vous est possible de le faire. Si, durant la sélection, vous affectez une valeur à $_, la liste sera modifiée. Mais cela est sans doute une mauvaise idée de modifier la liste passée en paramètre d'un grep car la fonction map est faite pour cela.
La fonction map prend en paramètre un bloc d'instructions et une liste ; elle applique le bloc à chacun des éléments de la liste (modification possible de la liste) et renvoie la liste constituée des valeurs successives de l'expression évaluée.
liste2 = map( { expression } liste1 ); (attention à ne pas mettre de virgule entre le bloc d'instructions et la liste)
La variable spéciale $_ vaut localement (dans le bloc d'instructions) chaque élément de la liste. La valeur de la dernière expression du bloc sera placée dans la liste résultat.
Voici quelques exemples :
@s = map( { -$_ } @t ); Le tableau @s aura pour valeurs les opposés des valeurs de @t.
@p = map( { $_."s" } @t ); Tous les mots de @t sont mis au pluriel dans @p.
@s = map( { substr($_,0,2) } @t ); Le tableau @s aura pour valeurs les deux premiers caractères des valeurs de @t.
@s = map( { fonction($_) } @t ); Vous pouvez écrire votre propre fonction ; les valeurs qu'elle renverra seront placées dans @s.
Dans les exemples qui précèdent, la liste d'origine n'est pas modifiée (sauf dans le dernier exemple où elle peut l'être dans la fonction). Voici un exemple de modification de liste :
map( { $_*=4 } @t ); Tous les éléments de @t sont multipliés par quatre.
Nous allons ici illustrer l'usage des listes et des tableaux par un exemple mathématique : le crible d'Ératosthène. Cet algorithme permet de calculer tous les nombres premiers inférieurs à un nombre donné n.
Son principe est le suivant : nous construisons tout d'abord la liste de tous les entiers de 2 à n. Ensuite, à chaque itération, nous supprimons de la liste tous les multiples du premier nombre de la liste et signalons ce premier nombre comme étant premier. Au premier tour de boucle, je supprime tous les nombres pairs et dis que 2 est premier. Au suivant, je supprime tous les multiples de 3 et affirme que 3 est premier. Au tour suivant, c'est le 5 qui est au début de la liste (4 étant multiple de 2, il a déjà été supprimé), j'enlève de la liste les multiples de 5 et annonce la primalité de 5 etc ... L'algorithme se termine lorsque la liste est vide, j'ai alors déterminé tous les nombres premiers inférieurs à n.
Voici la fonction réalisant cet algorithme en Perl :
sub Crible
{
my ($n) = @_;
# Liste initiale :
my @nombres = (2..$n);
# Liste des nombres premiers trouvés :
my @premiers = ();
# Tant qu'il y a des éléments (un tableau
# en contexte booléen vaut faux s'il est vide) :
while( @nombres )
{
# On extrait le premier nombre
my $prem = shift @nombres;
# On indique qu'il est premier
push @premiers, $prem;
# On supprime ses multiples
@nombres = grep { ( $_ % $prem )!=0 } @nombres;
}
# On renvoie la liste des nombres premiers
return @premiers;
}
|
Quiconque a déjà réalisé cet algorithme en C ou C++ comprendra la joie que cette concision procure ...