Developpez.com - Autres
X

Choisissez d'abord la catégorieensuite la rubrique :

Sommaire > Les listes associatives
        Qu'est-ce qu'une liste associative ?
        Comment conserver des listes associatives ?
        Comment définir et utiliser une table associative ?
        Et comment récupère-t'on le nombre d'éléments ?
        Mais comment accéder à un élément en particulier ?
        Encore une tranche ?
        Des fonctions, pour les listes et les tableaux associatifs ?

        


Qu'est-ce qu'une liste associative ?
auteur : 2Eurocents
Une liste associative, ou table de hachage est un type de variable particulier, qui contient des scalaires associés entre-eux par paires. Chaque paire est constitué d'un premier élement, nommé 'clef', qui servira de repère pour retrouver la valeur dans la table associative. Le second élément de la paire est la 'valeur' qui sera stockée dans la table. A chaque clef correspond une valeur. Il ne peut pas y avoir deux clefs identiques. Par contre, deux valeurs identiques peuvent être repérées par la deux clefs distinctes. La définition d'une paire ayant une clef déjà utilisée remplace la valeur associée à cette clef par la nouvelle valeur fournie. Une liste associative peut s'exprimer par une ou plusieurs paires de valeurs, séparées par des virgules, mises entre parenthèses comme : ('a', 'chaîne a', 'b', 'chaîne b', 'zz', 'chaîne zz') qui est l'expression d'une liste associative rassemblant les couples ('a', 'chaîne a'), ('b', 'chaîne b'), ('zz', 'chaîne zz'). Le problème de cette notation, c'est que l'on ne fait pas forcément la distinction entre listes et listes associatives. Cela peut être confortable pour passer d'une forme à l'autre, mais cela peut aussi être source d'erreur (notamment si l'on n'a pas mis un nombre pair de valeurs dans la table). C'est pourquoi il existe une autre notation qui met en évidence le caractère associatif des listes hachées : ('a' => 'chaîne a', 'b' => 'chaîne b', 'zz' => 'chaîne zz') Ainsi, l'association clef/valeur est explicitement mise en évidence. Il est, bien-sûr, nettement préférable d'utiliser cette notation. La nature des scalaires contenus dans une liste associative n'a absolument aucune espèce d'importance pour PERL. De même, la nature des valeurs utilisées comme clefs n'est pas critique. Les clefs doivent simplement correspondre à une chaîne de caractères, au sens large. PERL est même assez permissif sur la syntaxe des clefs et autorise soit leur écriture entre quotes simples ou doubles, soit leur écriture sans quotes si la chaîne est composé exclusivement des lettres A à Z, a à z, des chiffres de 0 à 9 et du tiret de soulignement "_". De la même manière que les listes simples, les listes associatives s'aplatissent les unes dans les autres :

(a => 'chaîne a', b => 'chaîne b', (A => 'chaîne A', B => 'chaîne B', C => 'chaîne C'), a => '2eme chaîne a');
définit une liste associative de cinq paires, dont les clefs sont 'a', 'b', 'A', 'B' et 'C'. La sixième paire définie utilisant de nouveau la clef 'a', la valeur associée 'chaîne a' sera remplacée par '2eme chaîne a'. La liste associative incluse a été aplatie et ses paires se retrouvent au même niveau que les paires de la liste principale. Pour inclure une liste associative dans une autre, en lui conservant son caractère 'liste', il sera nécessaire de faire appel à une notion avancée (que l'on verra ultérieurement) : les références. Comme pour les listes simples, une liste associative vide est définie par une paire de parenthèses vides : () définit une liste (simple ou associative) ne contenant aucun élément.


Comment conserver des listes associatives ?
auteur : 2Eurocents
Les listes associatives que l'on vient de voir sont, elles aussi, un peu volatiles ... nous ne les avons toujours pas rangées dans des variables. Elles ne peuvent pas être conservées dans une variable scalaire ordinaire et les ranger dans une table ordinaire n'aurait pas de sens, on perdrait le caractère associatif. PERL utilise un type de variable spécial pour gérer les listes associatives.


Comment définir et utiliser une table associative ?
auteur : 2Eurocents
Une table associative est une variable dont le sigill (le caractère qui précède le nom) est % :

my %association = (1 => 'aaa', 2 => 'bbb', 3 => 'ccc');
L'usage de cette table se fait de deux manières, selon que l'on veut la manipuler dans sa globalité, ou bien accéder à un élément particulier. L'accès global au tableau associatif se fait avec la notation en % :
  • %hash = (); pour vider un tableau de hachage,
  • %hash = (1 => 'premier', 2 => 'deuxieme', 3 => 'troisieme'); pour affecter une liste associative à un tableau du même métal,
  • %hash = %liste; pour affecter le contenu d'un tableau associatif à un autre tableau de hachage (attention, il s'agit d'une duplication du contenu),
  • %hash = (%hash1, %hash2, %hash3); pour affecter le contenu de trois tableaux associatifs dans un autre. Les clefs présentes dans plusieurs tableaux simultanément ne se retrouveront qu'en un exemplaire dans le hachage résultant avec pour valeur associée la dernière valeur rencontrée,
  • %hash = @liste; pour affecter une liste simple à une liste associative. Les éléments de la liste simple sont pris par paires, le premier de chaque paire étant utilisé comme clef, le second comme valeur.
Par contre, l'évaluation d'une table associative en contexte scalaire n'est, à priori, pas une idée pertinente.


Et comment récupère-t'on le nombre d'éléments ?
auteur : 2Eurocents
Puisque l'on ne peut pas évaluer la liste associative en contexte scalaire, contrairement aux listes simples, comment peut on récupérer le nombre d'éléments d'une liste associative ? PERL fournit deux fonctions particulières d'accès aux listes associatives : keys () et values (). keys () retourne une liste simple contenant toutes les clefs de la liste associative passée en paramètres. values () retourne une liste simple contenant toutes les valeurs de la liste associative passée en paramètres. Il suffit, pour connaître le nombre d'éléments de la liste associative de prendre le nombre d'éléments d'une de ces deux listes simples. Par contre, il est important de savoir que l'ordre des éléments dans les listes retournées par keys () et values () n'a rien à voir avec l'ordre dans lequel les éléments ont été ajoutés au hachage. Il s'agit d'un ordre qui permet à PERL d'optimiser ses recherches lorsque l'on souhaite accéder à un élément en particulier.


Mais comment accéder à un élément en particulier ?
auteur : 2Eurocents
Un tableau associatif 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 de hachage, et indiquer entre accolades la clef de celui que l'on souhaite récupérer : $hash{aaa} correspond donc au scalaire se trouve associé avec la clef 'aaa'. Notez bien que si la clef est bien une chaine de caractères, il n'est pas nécessaire de la mettre entre quotes tant qu'elle ne contient pas d'espaces ni de caractères susceptibles de géner l'interprèteur. Si la clef fournie n'existe pas, PERL créera paire d'éléments avec la clef, d'une part, et la valeur 'undef', d'autre part. Il est donc prudent, plutôt que de tenter d'accéder directement à des éléments dont on ne sait pas s'ils existent ou non, de les tester au préalable grâce à la fonction exists (). Ce qui ne vous empêche pas, par ailleurs, de faire des tests sur la définition valide de son contenu par la fonction defined ().


Encore une tranche ?
auteur : 2Eurocents
De même que pour les listes simples, il est tout à fait possible de manipuler des portions de listes associatives. Une petite limite toutefois, car le résultat d'une tranche de liste associative est en fait une liste simple des valeurs correspondant aux clefs demandées. Le mécanisme des "tranches" est tout à fait similaire et sa syntaxe est, encore une fois, tout à fait explicite : on précise les clef des éléments souhaités, séparées par une virgule, entre accolades.

@l = (1 => 'a', 2 => 'b', 3 => 'c', 4 => 'd', 5 => 'e', 6 => 'f'){2,4,0};
remplit @l avec les cles ('b', 'd', undef) de la liste associative fournie (ce qui n'a, dans cet exemple, aucun intérêt, puisque l'on a créé une liste hachée, juste pour le plaisir d'en garder quelques clefs). Vous remarquerez, dans cet exemple que l'on a pris une tranche de liste associative, correspondant à 3 clefs, et que le résultat est une liste simple contenant 3 valeurs. L'usage de clefs numériques ne doit pas vous faire perdre de vue qu'il ne s'agit pas d'indices, mais bien de valeurs associées. D'ailleurs, la clef 0 n'existant pas dans notre liste d'origine, elle est associée dans la tranche avec la valeur undef. Si l'on travaille sur un tableau associatif, dont on souhaite récupérer une tranche, la spécification de clefs 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 = (1 => 'a', 2 => 'b', 3 => 'c', 4 => 'd', 5 => 'e', 6 => 'f'); @sl = @l{2,4,0};
Attention, encore une fois aux sigills : la liste associative a un sigill %, alors que sa tranche perd le mécanisme associatif et s'exprime comme une liste simple avec un sigill @.


Des fonctions, pour les listes et les tableaux associatifs ?
auteur : 2Eurocents
Les fonctions shift (), unshift (), pop () et unpop () n'ont pas de raison d'être utilisées avec les tables associatives, l'ordre des éléments étant inconnu et déterminé automatiquement par PERL. Les fonctions split () et join () aussi n'ont plus de sens ... mais que reste-t'il alors ? delete () supprime toujours l'élément fourni. delete ($h{clef}) supprime l'élément de la table de hachage %h, dont la clef est 'clef'. Sa valeur n'est pas seulement mise à undef. Sa clef est également supprimée et les tests sur exists ($h{clef}) seront faux ensuite. reverse () change de signification. En effet, l'ordre des éléments étant quelconque, les inverser ne signifie rien. Du coup, reverse sert à faire l'échange entre les clefs et les valeurs. Les clefs deviennent les valeurs et les valeurs deviennent les clefs. Cela fonctionne très bien si les valeurs sont uniques. Dans le cas contraire, on ne peut pas prévoir quels seront les couples clef/valeur qui seront conservés pour les valeurs de départ en double. sort () ne peut pas trier une liste associative, mais permet, grâce à son bloc d'instructions, toutes sortes de tris sur les clefs ou sur les valeurs. Ainsi, pour un tri lexicographique des cles de %t, on peut faire :

@l = sort ( { $a cmp $b } keys(%t) );
qui fournit une liste des clefs triées. Pour un tri lexicographique des valeurs de %t, on peut faire :

@l = sort ( { $a cmp $b } values(%t) );
qui fournit une liste des valeurs triées - mais l'on a perdu les cles associées. Pour un tri lexicographique des clefs de %t, selon leurs valeurs associées, on peut faire :

@l = sort ( { $t{$a} cmp $t{$b} } keys(%t) );
qui fournit une liste des cles triées selon les valeurs associées croissantes - on a bien trié une liste simple de clef, mais le bloc d'instruction faisait la comparaison sur les valeurs liées. map () et grep () ne parcourront pas directent une table associative mais permettent tout traitement sur les listes de clefs ou les listes de valeurs. Pour reprendre l'exemple scolaire qui cloturait le chapitre sur les listes, en voici une ré-écriture plus pertinante au moyen de listes associatives :

# Préparation des données my %moyennes=(Valentine => 8, 'Chloé' => 16, Sophie => 19, Olivier => 12, 'Jérôme' => 6, Samuel => 15); # Fin de la préparation ... # Relevé des notes permettant le passage en classe supérieure # et liste des élèves admis, traitement simultané my @passent=grep ( { $moyenne{$_}>=10 } keys (%moyennes));
Cette ré-écriture du traitement est plus efficace (2 lignes de code, seulement, une pour la définition des données, une pour la sélection), plus facile à maintenir et plus sure que celle basée sur les tableaux. En outre, nous commençons à peine à ébaucher la puissance de PERL. Nous avons réalisé ce type de traitement de sélection, simplement en manipulant des données, sans construction de traitement complexes. Nous allons maintenant pouvoir ajouter tout ce qui est nécessaire au contrôle fin de l'exécution des traitements ...


        

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