Developpez.com - Autres
X

Choisissez d'abord la catégorieensuite la rubrique :

Sommaire > Listes
        Je n'ai pas qu'une valeur à traiter, comment fais-je ?
        Qu'est-ce qu'une liste simple ?
        Comment conserver des listes ?
        Comment définir et utiliser un tableau ?
        Mais comment accéder à un élément en particulier ?
        Comment gérer le nombre d'éléments du tableau ?
        Jongler avec listes et tableaux : c'est le cirque !
        Les listes, c'est bon, reprenez-en une tranche !
        Et les fonctions sur les listes et les tableaux ?
        Quelques fonctions surpuissantes !

        


Je n'ai pas qu'une valeur à traiter, comment fais-je ?
auteur : 2Eurocents
Nous avons vu que PERL gérait des variables scalaires, c'est à dire atomique. Il est aussi capable de gérer des types de variables un peu plus complexes, qu'il sera possible de décomposer en scalaires. Ces types "agrégés" sont principalement au nombre de deux :
  • les listes simples, que l'on rapprochera de la notion de table et qui font l'objet de ce chapitre,
  • les listes associatives, ou tables associatives, qui feront l'objet du chapître suivant.


Qu'est-ce qu'une liste simple ?
auteur : 2Eurocents
Une liste simple, ou ordinaire est un type de variable particulier, qui contient des scalaires. Une liste s'exprime par une ou plusieurs valeurs, séparées par des virgules, mises entre parenthèses comme : (1, $toto, 'Hello World', "Histoire de $toto", 3.1415) qui est l'expression d'une liste rassemblant la valeur entière 1, le contenu du scalaire $toto, la chaîne 'Hello World', la chaîne constituée par la jonction entre "Histoire de " et le contenu du scalaire $toto, et pour finir, la valeur flottante 3.1415. La nature des scalaires contenus dans une liste n'a absolument aucune espèce d'importance pour PERL, comme on vient de le voir, mais fonctionnellement le programmeur a peut être intérêt à veiller à la consistance des données. Il est possible de constituer les listes en utilisant quelques opérateurs spécifiques :
  • L'opérateur d'intervalle (que l'on a pas encore abordé) '..' crée explicitement une liste commençant à la valeur qui le précède et se terminant à la valeur qui le suit - sous réserve d'une certaine cohérence de type :
    • (5..15) crée une liste de tous les entiers de 5 à 15.
    • (-5..5) crée une liste des entiers de -5 à 5.
    • (a..f) crée une liste de tous les caractères de 'a' à 'f'.
    • (A..F) crée une liste de tous les caractères de 'A' à 'F' (distincte de la précédente - attention à la casse !)
    • (a..F) crée une liste des caractères de 'a' à 'z' et (A..f) crée une liste des caractères de 'A' à 'Z'. En effet, la génération de liste part de la première valeur, et monte jusqu'à essayer de rencontrer la dernière. Malheureusement, elle bute en cours de route sur la limite de cohérence de l'ensemble ('z' pour les minuscules et 'Z' pour les majuscules) et elle s'y arrête.
    • (aaaa..zzzz) crée une liste de toutes les combinaisons de 4 lettres minuscules (déjà beaucoup ... qui a dit "attaque brute-force ?)
  • L'opérateur de multiplication 'x', impose quelques précautions de parenthèsage :
    • ('A' x 3, 'B' x 2, 'C') crée une liste constituée de 'AAA', 'BB' et 'C'.
    • (('A') x 3, ('B') x 2, 'C') crée une liste constituée de 'A', 'A', 'A', 'B', 'B' et 'C'. Les parenthèses introduites créent des listes d'un seul élément ('A' ou 'B') qui sont elles mêmes multipliées ou dupliquées. - l'introduction de sous-listes, ainsi qu'on vient de l'ébaucher. Les listes étant des éléments entre parenthèses, il est possible d'introduire des listes dans des listes. Les listes qui sont ainsi insérées sont alors "aplaties" dans la liste réceptacle. Cela signifie que l'on n'introduit pas la liste en tant que telle, mais plutôt son contenu.
    Cette notion d'aplatissement des listes les unes dans les autres est un concept essentiel dans le traitement des ensembles de valeurs par PERL : (1, 2, (3, 4, (5, 6), 7), 8) est une liste dans laquelle on introduit une sous-liste qui contient elle-même une sous-liste. Au final, on obtient une liste "plate", c'est à dire sans niveaux d'imbrication, de 8 éléments. Pour conserver le caractère 'liste' d'un ensemble de valeurs introduit dans un autre, il sera nécessaire de faire appel à une notion avancée (que l'on verra ultérieurement) : les références.
  • des parenthèses vides pour définir une liste vide :
    • () définit une liste ne contenant aucun élément.


Comment conserver des listes ?
auteur : 2Eurocents
Les listes que l'on vient de voir sont un peu volatiles ... nous ne les avons pas encore rangées dans des variables. Elles ne peuvent pas être conservées dans une variable scalaire ordinaire. PERL a besoin de types de variables spéciaux pour gérer les listes. Pour les listes simples, le type utilisé est le type tableau, ou vecteur.


Comment définir et utiliser un tableau ?
auteur : 2Eurocents
Un tableau est une variable dont le sigill (le caractère qui précède le nom) est @ :

my @tableau = (1, 2, 3);
L'usage de ce tableau se fait de deux manières, selon que l'on veut le manipuler dans sa globalité, ou bien accéder à un élément particulier. L'accès global au tableau se fait avec la notation en @ :
  • @tableau = (); pour vider un tableau
  • @tableau = (1, 2, 3); pour affecter une liste à un tableau
  • @tableau = @liste; pour affecter le contenu d'un tableau à un autre tableau (attention, il s'agit d'une duplication du contenu)
  • @tableau = (@liste1, @liste2, @liste3); pour affecter le contenu de trois tableaux dans un autre
  • @tableau = (@liste_avant, @tableau, @liste_apres); pour ajouter des listes ou des tableaux à un tableau, avant et après les valeurs actuelles
Une erreur fréquente chez les débutants consiste à affecter un tableau à une valeur scalaire :

$i = @tableau;
Ce qui ne fonctionne pas comme on pourrait le souhaiter ... En contexte scalaire (quand on l'affecte à un scalaire, ou bien lorsqu'on l'écrit scalar(@tableau)), un tableau retourne le nombre d'éléments qu'il contient. En contexte scalaire de chaîne, le tableau retourne la liste de ses éléments, séparés par des blancs. Comment forcer ce contexte de scalaire de chaîne ? Tout simplement en encadrant le tableau par des doubles-quotes :

$contenu = "@tableau";

Mais comment accéder à un élément en particulier ?
auteur : 2Eurocents
Un tableau contient des scalaires. Pour en récupérer un en particulier, il faut indiquer que l'on veut un scalaire, donc utiliser le sigill $ devant le nom du tableau, et indiquer entre crochets lequel en particulier (rang de rangement dans le tableau, le premier ayant pour rang 0) : $tableau[5] correspond donc au scalaire se trouvant en 6e position du tableau. Les indices de tableau sont donc des entiers positifs, commençant à 0. Il est possible d'utiliser des entiers négatifs pour effectuer un parcours à rebours du tableau - donc en commençant par la fin :
  • $tableau[-1] désigne ainsi le dernier élément du tableau,
  • $tableau[-2] l'avant-dernier,
  • $tableau[-3] l'antépénultiemme, etc.


Comment gérer le nombre d'éléments du tableau ?
auteur : 2Eurocents
C'est tout simple. On ne le gère pas, et on laisse PERL s'occuper de tout. Il est déjà facile d'accéder aux valeurs du tableau, que ce soit en commençant à un bout ou à l'autre. On a aussi vu qu'il était facile de connaître le nombre d'éléments (scalar(@tableau)). Il est aussi possible de connaître l'indice du dernier élément du tableau, grâce à une combinaison spéciale de sigills devant le nom de tableau : $# $#tableau retourne donc le rang du dernier élément de tableau (pour mémoire, c'est un nombre, donc un scalaire, d'où le $, et c'est un indice, donc un numéro, d'où le # qui signifie numéro en notation commerciale américaine). Maintenant, accéder à des éléments hors des bornes du tableau ne pose pas de problème non plus. PERL crée automatiquement tous les éléments intermédiaires en leur affectant une valeur indéfinie (undef) :

my @liste=(1..5); $liste[10] = 1;
crée une liste de 11 éléments (1, 2, 3, 4, 5, undef, undef, undef, undef, undef, 1). Pour savoir si un élément de tableau existe réellement, c'est à dire s'il a été créé explicitement, et pas par extension de tableau, PERL met à notre disposition la fonction exists() qui retourne vrai si l'élément a bien été créé, faux s'il a été obtenu par extension. Si vous vous souvenez bien des initialisations de variables, il est possible de laisser une variable non initialisée, voire de la vider en lui affectant la valeur undef. Dans ce dernier cas, s'il s'agit d'un élément de tableau, le test avec la fonction defined () retourne faux, puisque la variable est indéfinie, mais le test avec exists () retourne vrai puisque la variable a été explicitement indéfinie. Il faut donc impérativement faire la distinction entre test de définition (defined ()) et test d'existence (exists ()).


Jongler avec listes et tableaux : c'est le cirque !
auteur : 2Eurocents
L'équivalence liste/tableaux, la gestion automatique de la taille des tableaux, l'aplatissement des listes les unes dans les autres permettent à PERL des constructions très intéressantes :
  • ($v1, $v2, $v3) = (10, 20, 30); est une affectation de trois valeurs, simultanément. Notez que si vous avez plus d'éléments dans la liste de gauche de l'affectation que dans celle de droite, les éléments en trop à gauche recevront la valeur undef. Inversement, si vous avez plus d'éléments à droite du signe égal qu'à gauche, les dernières valeurs à droites seront ignorées.
  • ($v1, $v2, $v3) = ($v2, $v3, $v1); est une façon intéressante de mélanger/permuter des valeurs de variables scalaires, que vous en ayez 2, 3, ou n à échanger (enfin, pas trop, quand même, pour que ça reste lisible et maintenable).
  • ($valeur, @tableau) = @tableau; est une très jolie façon de supprimer la première valeur d'un tableau, tout en la récupérant.
  • (@tableau, $valeur) = @tableau; ne sert à rien, car le tableau en début de liste récupère toutes les valeurs de l'affectation. Il ne reste rien pour remplir $valeur, qui vaudra alors undef.
  • ($v1, $v2) = @tableau; mets dans $v1 et $v2 les deux premiers scalaires contenus dans le tableau (sans modifier celui-ci), ce qui est à rapprocher du premier exemple donné ici.
  • ($v1, undef, $v2, undef, undef, $v3) = @tableau; mets dans $v1 la première valeur du tableau, dans $v2 la troisième et dans $v3 la sixième. Les autres scalaires du tableau sont ignorés.
  • my ($a, $b, $c); et my ($a, $b, $c) = (1, 2, 3); sont les seules façons correctes de faire des déclarations multiples et simultanées de variables.


Les listes, c'est bon, reprenez-en une tranche !
auteur : 2Eurocents
Si vous souhaitez manipuler des portions de listes, il existe un mécanisme délicatement nommé "tranches" qui vous permet de récupérer une liste qui est un sous-ensemble d'une autre. Ce mécanisme a une syntaxe un peu spéciale mais tout à fait explicite. Si l'on travaille sur une liste (ensemble exprimé entre parenthèses), il suffit de suffixer la liste par une expression des indices des valeurs souhaitées, entre crochets. Cette expression peut être constituées d'indices distincts, séparés par des virgules, et/ou indiquer des intervalles d'indices au moyen de l'opérateur d'intervalles .. :
  • @l = ('a', 'b', 'c', 'd', 'e', 'f')[2,4,0]; remplit @l avec la liste ('c', 'e', 'a')
  • @l = ('a', 'b', 'c', 'd', 'e', 'f')[0, 2..4]; remplit @l avec la liste ('a', 'c', 'd', 'e')
Si l'on travaille sur un tableau, dont on souhaite récupérer une tranche, la spécification d'indices a la même forme, mais le nom de tableau doit être précédé du sigill @, car on n'accède pas à un scalaire, mais à un tableau, constitué par cette fameuse tranche :

@l = ('a', 'b', 'c', 'd', 'e', 'f'); @sl = @l[2,4,0];
Il est même possible de faire des remplacements dans un tableau, au moyen du mécanisme de tranches :

@l = ('a', 'b', 'c', 'd', 'e', 'f'); @l[2,4,0]=(1,2,3);
modifie la liste @l qui vaut alors (3, 'b', 1, 'd', 2, 'f').

@l = qw(a b c d e f); @l[1..3]=(1,2,3);
modifie la liste @l qui vaut alors ('a', 1, 2, 3, 'e', 'f'). Pire encore, les mécanismes de tranches et d'aplatissement des listes permettent d'agrandir ou de rétrécir un tableau par le milieu :
  • @l = qw(a b c d e f);
  • @l = (@l[0..2], (1, 2, 3), @l[4..5]); modifie la liste @l pour qu'elle vaille ('a', 'b', 'c', 1, 2, 3, 'e', 'f') - en gros, on remplace l'élément d'indice 3 par la liste (1, 2, 3).
  • @l = qw(a b c d e f);
  • @l = (@l[0..1], @l[4..5]); supprime 'c' et 'd' dans le tableau @l.


Et les fonctions sur les listes et les tableaux ?
auteur : 2Eurocents
PERL contient de nombreuses fonctions gérant les listes et les tableaux. En voici quelques unes des plus utiles :
  • qw () permet de créer une liste à partir d'une chaîne de caractères. La chaîne est découpée selon les blancs, les tabulations et les sauts de ligne. Chaque 'mot' devient alors un élément de liste. Attention, seuls les espaces, tabs, et sauts de lignes sont pris en compte. Aucune protection par des quotes ou doubles-quotes n'est efficace. On peut ainsi écrire : @l = qw (Cela est beau et bon); plutôt que @l = ('Cela', 'est', 'beau', 'et', 'bon');
  • shift () permet de supprimer le premier élément d'une liste et de retourner sa valeur. Ainsi, $val = shift (@t); est finalement équivalent à ($val, @t) = @t;
  • unshift (), au contraire, permet d'ajouter un ou plusieurs éléments au début d'un tableau. unshift (@t, 1, 2, 3); ajoutera (1, 2, 3) au début du tableau @t.
  • pop () permet de supprimer le dernier élément d'une liste et de retourner sa valeur. Finalement, $val = pop (@t); est équivalent à ($val, @t) = ($t[$#t], @t[0..($#t-1)]); mais c'est beaucoup plus lisible !!!
  • unpop (), à l'inverse, permet d'ajouter un ou plusieurs éléments en fin de tableau.
  • delete () permet de supprimer un élément d'un tableau. Non seulement sa valeur est alors positionnée à undef, il rend donc vrai au test not defined (), mais il est réellement supprimé, c'est à dire qu'il retourne aussi vrai au test not exists (). delete ($t[2]); supprimera le troisième élément du tableau @t.
  • split () permet de découper une chaîne selon des marqueurs spécifiques et retourne la liste des éléments résultants de ce découpage. Le marqueur spécifique est indiqué sous la forme d'une expression rationnelle. Sans entrer dans le détail de celles-ci, voici des exemples :
    • split (/ \t\n/, "chaîne à découper"); effectue le même travail que qw sur la "chaîne à découper".
    • split (/:/, "root:password:0:0:/bin/bash"); effectue le découpage d'une ligne de fichier de mots de passes *n*x selon les différents champs séparés par des ":".
  • join () effectue la tache inverse de split (). Elle rassemble les éléments de la liste fournie en les concaténant, tout en les séparant par la chaîne indiquée comme premier paramètre. Ainsi join ('...', 1, 2, 3); créera la chaîne "1...2...3".
  • reverse () inverse l'ordre de tous les éléments d'un tableau.


Quelques fonctions surpuissantes !
auteur : 2Eurocents
  • sort () trie les éléments d'une liste. La fonction sort () retourne une liste triée des éléments fournis en paramètre. Ces deux listes peuvent être distinctes, ou bien la liste d'origine peut être écrasée par le résultat du tri si elle est destinataire de l'affectation. La fonction sort peut prendre un argument spécial qui est le bloc de comparaison a effectuer entre les éléments de la liste à trier. Il est ainsi possible de trier selon des critères tout à fait personnalisés. Par défaut, le tri se fait dans l'ordre "lexicographique" (ordre alphabétique, étendu aux nombres dont les chiffres sont traités comme des caractères et non comme des nombres). Le bloc de comparaison personnalisé doit être précisé entre accolades, avant la liste d'éléments à trier, sans être séparé de celle-ci par une virgule. Pour la comparaison, ce bloc utilise deux variables internes de PERL, $a et $b, qui ne sont définies que dans ce bloc et masquent toute variable $a ou $b propre à l'utilisateur. Ce bloc effectue donc un test quelconque, basé sur $a et $b, dont le résultat peut prendre trois valeurs :
    • positif si $a est avant $b dans l'ordre de tri souhaité
    • nul si $a et $b sont équivalents
    • négatif si $a est après $b dans l'ordre souhaité.
    Ces trois valeurs de résultat de test correspondent aux résultats des opérateurs de test <=> et cmp vus précédemment. Ainsi, pour un tri lexicographique du tableau @t, on peut faire : @l = sort ( @t ); ou bien @l = sort ( { $a cmp $b } @t ); qui a l'avantage d'être totalement explicite et donc plus clair à maintenir. Pour un tri lexicographique inversé, on peut aussi bien écrire : @l = reverse ( sort ( { $a cmp $b } @t ) ); que @l = sort ( { $b cmp $a } @t ); Pour un tri numérique : @l = sort ( { $a <=> $b } @t ); Pour un tri un peu spécial, en supposant que @t contienne des indices d'un tableau et que l'on souhaite un tri de ces indices selon les valeurs numériques du tableau : @l = sort ( { $tableau[$a] <=> $tableau[$b] } @t ); @l contient alors les valeurs de @t triées dans un ordre tel qu'elles indicent des valeurs croissantes dans @tableau. C'est très indirect et ce n'est pas forcément très naturel au début, mais c'est extrêmement puissant. Et pour finir, bien que l'on ait pas encore abordé la définition de fonctions personnalisées, il est possible d'appeler une fonction définie par le programmeur : @l = sort ( { mafonction ($a, $b) } @t );
  • map () parcours les listes. La fonction parcours l'intégralité de la liste fournie en paramètres et lui applique un traitement fourni par l'utilisateur. Elle retourne la liste des éléments traités. Comme pour la fonction sort (), il faut fournir un bloc de traitement, exprimé entre accolades et qui ne sera pas séparé de la liste à traiter. Le traitement effectué peut être totalement quelconque, porter sur des chaînes, des nombres ou des listes, utiliser une fonction interne à PERL ou une fonction utilisateur, etc. Dans ce bloc, l'élément en cours de traitement se trouve dans la variable interne à PERL nommée $_. @l = map ( { $_ + 1 } @t ); et @l contiendra tous les éléments de @t augmentés de un. @l = map ( { ($_) x 3 } @t ); et @l contiendra tous les éléments de @t, mais dupliqués 3 fois. $i=0; @l=map ( { $i += $_ } @t ); et @l contiendra la somme des éléments de @t d'indice inférieur ou égal. Si le bloc de traitement effectue des modifications sur $_, celles-ci seront répercutées sur la liste d'origine. La plus grande rigueur est donc de mise ... @l = map ( { $_ .= "+" } @t); suffixe toutes les valeurs de @t avec un "+". Les nouvelles valeurs se trouvent aussi bien dans @t que dans @l. @l = map ( { $i = $_; $_.="+"; $i."-" } @t ); est plus complexe. Ce traitement, pour chaque valeur de @t, la mémorise dans $i, la modifie dans le tableau @t en la suffixant par un "+" et ressort, pour la liste de destination (@l) la valeur mémorisée dans $i, mais suffixée par un "-".
  • grep () parcours les listes et sélectionne les éléments. La fonction parcours l'intégralité de la liste fournie en paramètres et lui applique un traitement de sélection fourni par l'utilisateur. Elle retourne la liste des éléments qui répondent aux critères de sélection fournis. Comme pour les fonctions sort () et map, il faut fournir un bloc de traitement, exprimé entre accolades et qui ne sera pas séparé de la liste à traiter. Il est aussi possible de fournir, à la place de ce bloc, une expression rationnelle ... dont nous verrons le mécanisme plus tard, mais à force d'en parler, impatience va finir par monter ... Dans le bloc de sélection, l'élément en cours de traitement se trouve dans la variable interne à PERL nommée $_. @l = grep ( { $_ != 0 } @t ); et @l contiendra tous les éléments non nuls de @t. @l = grep ( /mot-clef/ @t ); et @l contiendra tous les éléments de @t dans lesquels "mot-clef" est présent, grâce au mécanisme des expressions rationnelles. Voici, à titre d'exemple un traitement plus complexe (bien que tout à fait artificiel, PERL ayant des mécanismes que nous allons voir bientot qui permettent de le faire encore plus efficacement) : # Préparation des données my @eleves=qw (Valentine Chloé Sophie Olivier Jérôme Samuel); my @notes=(8, 16, 19, 12, 6, 15); my @indices=(0..$#eleves); # Fin de la préparation ... # Relevé des notes permettant le passage en classe supérieure my @passage=grep ( { $notes[$_]>=10 } @indices); # Liste des élèves admis my @passent=map ( { $eleves[$_] } @passage); Cependant, ce traitement est vulnérable car le lien entre les notes et les élèves est assez lache. Il s'agit d'un indice, une position dans le tableau. Si le tableau est trié, tout le traitement s'effondre. C'est pourquoi il existe en PERL une structure de données plus efficace pour associer des valeurs entre elles : les listes associatives.


        

Consultez les autres F.A.Q's

Ce document issu de www.developpez.com est soumis à la licence GNU FDL traduit en français ici
Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement.
Contacter le responsable de la rubrique Autres