Les listes en Tcl

Ce tutorial est libre et doit être redistribué gratuitement, vous devez laisser le nom de son auteur visible et indiquer la source de celui-ci si vous l'intégrez à vore site ou le distribuez d'une quelque façon que ce soit.

Ce tutoriel a pour cibles des personnes ayant déjà des conaissances en Tcl. Les listes étant la donnée primordiale en Tcl, j'ai écrit ce turorial pour expliqueur clairement leurs fonctionnements aux personnes souhaitant approfondir leurs conaissances.

Qu'est-ce qu'une liste ?

On peut représenter une liste Tcl exactement de la même manière qu'une liste de course, imaginez que vous devez aller au supermarché faire des courses pour acheter des pommes, des citrons et des poires, vous écrivez sur un petit bout de papier ces trois éléments et vous allez faire vos courses.

En Tcl c'est exactement pareil, sauf que le petit bout de papier est une variable:

set maListe [list "pommes" "citrons" "poires"]

On vient de créer une liste de trois éléments, on peut aller faire nos courses !

Représentation interne des listes

Les listes Tcl sont stockées dans des variables, comme n'importe quelles autre données, seulement Tcl va formatter le contenu de cette variable d'une certaine façon. Ce n'est pas une erreur d'utiliser des fonctions comme string sur une variable contenant une liste, mais c'est réellement déconseillé. Tcl ne génerera pas d'erreurs mais vous risquez d'avoir des résultats surprenants.

Explications

Nous allons créer une liste de trois éléments, contenant certains caractères spéciaux, comme des espaces et des '[':

set maListe [list "bonjour tout"  "le monde,"  " il est \[15h18\]"

La variable $maListe contiendra une liste, qui sera représentée par:

{bonjour tout} {le monde,} { il est [15h18]}

Ceci est donc le contenu exacte de la variable $maListe. Comme vous le voyez, Tcl protège chaque élément de la liste avec des {}, afin de délimiter précisément  ceux-ci. Si vous travaillez manuellement sur cette liste, vous risquez de ne pas gérer correctement les caractères spécieux intégrés par Tcl. Par example imaginons que vous ajoutiez un "{" à la fin de la chaine avec append comme ceci:

append maListe  \{

Le contenu de la variable devient alors:

{bonjour tout} {le monde,} { il est [15h18]}{

Si après ça vous utiliser par exemple [lindex] pour récupérer le premier élément de la liste, tcl va générer une erreur:

puts [lindex $maListe 0]
Erreur: unmatched open brace in list

Tout ça pour vous expliquerqu'il ne faut pas modifier manuellement des variables de listes, mais toujours utiliser des fonctions comme lappend, linsert, lreplace etc. De cette façon Tcl protegera correctement les données contenues dedans.

L'inverse est vrai aussi

Si ce que je viens de vous expliquer est vrai, l'inverse l'est aussi: vous ne devez pas utiliser des fonctions de listes sur des chaines de caractères pour les mêmes raisons ! Imaginons que j'ai une chaine de caractère qui contient ceci:

set maChaine "salut Merwin{Away"

Si vous faites un lindex sur $maChaine vous obtiendrez la même erreur que précédemment. Soyez donc toujours attentif lorsque vous utilisez une commande qui attend une chaine, comme string, append, etc, que ce soit bien une chaine dans la variable, et inversement, vérifiez que vous passez bien des listes aux commandes de gestions des listes (lindex, lappend, etc).

Les commandes des gestions des listes

lindex

La commande lindex retourne un élément d'une liste en fonction de son index dans la liste. L'index peut être un chiffre compris entre 0 et la taille de la liste - 1. Vous pouvez aussi utiliser le mot clé end, ou end-n pour réprésenter le dernier élément, ou ceux qui le précède.

Exemples:

set maListe [list "salut" "tout" "le" "monde"]
puts [lindex $maListe 0] ; # Affichera "salut"
puts [lindex $maListe end] ; # Affichera "monde"
puts [lindex $maListe end-1] ; #Affichera "le"

lappend

La commande lappend vous permet d'ajouter un élément à une liste, ajoutons des oignons à notre liste du début:

lappend maListe "oignons"

La liste contiendra alors 4 éléments. Note: Vous devez passer le nom de la variable en paramètre, et non pas son contenu, la variable est directement modifiée à l'appel de la commande.

lreplace

La commande lreplace comme son nom l'indique sert à remplacer un élément dans une liste. Si vous souhaitez effacer un élément d'une liste, vous ne devez pas donner de paramètre, ce qui l'effacera. Vous devez spécifier une range d'index, allant de 0 à où vous voulez, et autant de valeurs que d'index à remplacer.

set maListe [lreplace $maListe 0 1 "peche" "pruneau"]

Dans cet exemple on remplace le premier et le second élément respectivement par peche et pruneau.

Note: Le premier index est 0, et non 1 ! Vous pouvez utiliser le mot clé end pour identifier le dernier index, et end-n pour ceux qui le précendent.

set maListe [lreplace $maListe end end]

Dans cet exemple on efface le dernier élément de la liste. Pour plus d'informations consultez la documentation de lreplace.

llength

Cette fonction retourne simplement le nombre d'éléments d'une liste.

set maListe [list "bonjour les " "enfants"]
puts [llength $maListe]

Cet exemple affichera "2".

lsearch

La fonction lsearch est assez complexe à utiliser, elle permet de rechercher un élément dans une liste en fonction d'un paramètre, je ne vous donnerai ici que l'exemple concernant la recherche d'un élément par son nom, mais d'autres possibilitées existent.

set pommeIndex [lsearch -exact $maListe "pomme"]

Cette exemple placera dans $pommeIndex l'index de l'élément "pomme", c'est à dire 0 dans notre liste de départ.

Note: Si l'élément n'existe pas, la fonction retourne -1 (Comme 0 peut être un index !)

split

Voila une fonction intéressante, qui vous sera très très utile. La fonction split permet de transformer une chaine en liste. Son opposé est la fonction join, que l'on verra juste après. Imaginons que nous ayons la chaine suivante, je vais prendre l'exemple d'une phrase tapée sur IRC:

set machaine "salut tout le monde, comment allez-vous ?"

Nous allons transformer cette chaine en liste, en utilisant split comme ceci:

set list_chaine [split $machaine " "]

$liste_chaine contient alors une liste de 7 éléments. Dans split nous avons spécifier que le caractère séparateur serait l'espace, Tcl a donc tronqué la chaine en plusieurs éléments d'une liste, dont chacun est un mocreau de la chaine, séparé par le caractère séparateur (ici, espace).

Note: Si vous ne spécifiez pas de caractère sépérateur par defaut, c'est l'espace.

Nous aurions aussi pu utiliser, par exemple la virgule comme sépérateur, comme ceci:

set list_chaine [split $machaine ","]

Dans ce cas la, $list_chaine aurait été une liste de 2 éléments, le premier étant "salut tout le monde", et le second " comment allez-vous ?". Cette fonction est très utile et doit être utilisée dès que vous souhaitez traiter une chaine comme une liste.

Note: La représentation interne de $list_chaine dans le second cas aurait été la suivante:

{salut tout le monde} { comment allez-vous?}

join

La fonction join fait exactement l'inverse de split, elle prend une liste et rejoint les éléments en utilisant le sépérateur spécifié, ou espace par default. Reprenons comme exemple notre phrase, que l'on aura préalablement splittée sur le caractère espace, soit le premier exemple du paragraphe précédent.

set list_chaine [split "salut tout le monde, comment allez-vous ?"]
puts [join $list_chaine ","]

Aura comme résultat:

salut,tout,le,monde,comment,allez-vous,?

Le résultat de join est une chaine, une fois que vous utilisez join vous devez plus utiliser des commandes de listes sur le résultat.

list

Pour finir, la commande list sert simplement a créer une liste, chaque paramètre passée à la commande sera un élément de la liste. Voir mon premier exemple avec les fruits. Je vous conseil fortement de toujours créer vos listes avec list plutot que de le faire manuellement en mettant directement dans la variable la représentation interne de la liste.

Bon exemple:

set liste [list "bonjour les" "enfants"]

Mauvais exemple:

set liste "{bonjour les} {enfants}"

Exercices

Afin de vous entrainer à manipuler les listes, je vous proposes quelques petits exercices pour vous entrainer à utiliser les commandes ci-dessus, pour voir si vous avez tout compris !

Exercice 1

Réaliser une procédure qui retourne 1 si un mot est présent dans une phrase, et 0 sinon.

Syntaxe:

motDansPhrase <mot> <phrase>

Aide: Les variable $mot et $phrase contiendront des chaines, à vous de le/less transformer en liste(s) si nécéssaire !

Exercice 2

Réaliser une procédure qui retourne le nombre de mots présents dans une phrase.

Syntaxe:

nombreMots <phrase>

Ici aussi, $phrase est une chaine.

Exercice 3

Un exercice plus difficile pour voir si vous avez vraiment compris les listes.

Vous devez écrire une procédure qui, pour une phrase donnée, affiche le nombre de mots et de lettres qui la compose. Pour cette même phrase, votre procédure doit ensuite inverser l'ordre des mots, c'est à dire que le premier mot devient le dernier, etc.

Exemple:

exo3 "Salut tout le monde !"

Affichera en sortie:

Phrase: "Salut tout le monde !" (5 mots, 21 lettres)
Phrase inversée: "! monde le tout Salut"

Consignes:

Interdiction d'utiliser la commande lreverse, ainsi que la commande string.

Petits exemple avec Eggdrop

Il est fréquent pour les débutants qui codent en tcl d'avoir certaines erreurs dans leurs petit script Eggdrop, voici un exemple, vous devriez voir l'erreur tout seul si vous avez bien suivit ce tutorial.

bind pub - "!op" proc:op
proc proc:op {nick uhost handle channel arg} {
    set target [lindex $arg 0]
    putserv "MODE $channel +o $target"
}

Comme vous pouvez le constater ici: erreur de débutant, $arg est une chaine de caractère ! Utiliser lindex dessus causera des problèmes si la chaine contient des caractères spéciaux, comme { ou } ! On doit bien sur utiliser split avant.

Autre exemple: séparer l'ident et l'host de $uhost:

proc proc:op {nick uhost handle channel arg} {
    set split_uhost [split $uhost "@"]
    set ident [lindex $split_uhost 0]
    set host [lindex $split_uhost 1]
    putserv "PRIVMSG $channel :Vous ($nick - $ident / $host) voulez opper $arg !"
}

En esperant que ce tutoriel vous aura aider à mieux comprendre les listes en Tcl, vous devrez cependant consulter différentes documentations/tutoriaux afin de mieux comprendre leurs fonctionnements. Il est par exemple de faire des listes de listes, c'est à dire des listes dont chaque élément est lui même une liste.

Auteur

Merwin (merwin.irc@gmail.com) - http://www.ircz.fr