Deux constructeurs préexistent pour les listes:
-
list
- cons
- et on peut leur ajouter une fonction intimement liée: append
Ces trois constructeurs ont des types bien différents: ils ne doivent
pas être pris l'un pour l'autre. Le premier est une notation dont on
pourrait se dispenser (quoique fastidieusement) à l'aide du second. Le
troisième peut être défini à l'aide du second et avec le schéma
récursif général des listes. En conclusion, seul le second est
vraiment indispensable mais disposer des trois est un gage indéniable
de confort et d'ouverture d'esprit: il en faut pour choisir le bon
constructeur dans la bonne situation!
Le constructeur
list est une fonction n-aire qui construit
d'un seul coup d'un seul une liste à l'aide de ses arguments. Les
arguments de
list sont, dans l'ordre, réunis en une
liste. Ainsi:
(list 1 (+ 1 1) (/ 6 3)) ® (1 2 3)
(list (list (- 3 1)) (list 2)) ® ((2) (2))
(list) ® ()
Puisque les listes sont homogènes, le type du constructeur
list est
a×...®LISTE[a]. Dans ce
type, les points de suspension indiquent la
possibilité d'un nombre quelconque d'arguments mais tous de type
a. La liste vide d'entiers est aussi la liste vide de listes
d'images, la liste vide
() ne dit pas quel est sa nature
exacte. On dit que la liste vide est
polymorphe.
Le constructeur
cons construit les listes termes par termes. Il
est très rudimentaire c'est pourquoi il est plus primitif que
list. Sa particularité est de prendre une valeur d'un certain
type
t et une liste de type
LISTE[t] et de
créer une nouvelle liste de même type (à savoir
LISTE[t])
dont le premier terme est le premier argument et dont les autres
termes sont ceux de la liste de second argument. Le type de
cons est donc
t ×LISTE[t] ®LISTE[t].
La fonction
cons a été nommée ainsi parce qu'elle
cons-truit des listes.
On peut toujours se passer de
list au profit de
cons
en explicitant comment la liste est construite pas à pas. Ainsi:
l'expression
(list (* 2 1) (+ 2 2)) peut elle aussi s'écrire
(cons (* 2 1) (cons (+ 2 2) (list))). [On verra plus tard
comment écrire autrement
(list) sans faire intervenir
list!] Ce que l'on vient de faire pour créer une liste de
deux termes est encore lisible mais devient vite fastidieux pour une
plus longue liste. Ainsi:
(list 1 2 3 4 5 6 7 8 9) º
(cons 1
(cons 2
(cons 3
(cons 4
(cons 5
(cons 6
(cons 7
(cons 8
(cons 9 (list))))))))))
Heureusement que les éditeurs de textes illuminent les parenthèses
associées sinon l'écriture serait un enfer (ce le fut avant
l'invention des éditeurs WYSIWIG lorsque l'on comptait les parenthèses
à la main!)
Le dernier constructeur est la fonction bien utile
append qui
prend deux listes et renvoie une nouvelle liste dont les premiers
termes sont ceux de son premier argument et dont les derniers termes
sont ceux de son dernier argument. La fonction
append prend
donc deux listes de types compatibles, disons des
LISTE[a] et renvoie une liste de même type
LISTE[a]. Ainsi:
(append (list (* 1 1) (+ 1 1))
(list 3) ) ® (1 2 3)
(append (list) (list 1 2)) ® (1 2)
(append (list 1 2) (list)) ® (1 2)
(append (list) (list)) ® ()
Comment choisir son constructeur ? Cela dépend bien sûr de ce dont on
dispose pour cela. Si l'on doit créer une liste dont on connaît les
termes (dans l'ordre), utilisez
list. Si l'on doit créer une
liste terme par terme en les ajoutant par la gauche,
cons
s'impose. Enfin si l'on dispose déjà de listes toutes faites et que
l'on cherche à les réunir, il est possible d'employer
append.
La fonction
append est assez coûteuse car, lorsque l'on
regarde sa définition que voici:
;;;
(define (append l1 l2)
(if (pair? l1)
(cons (car l1) (append (cdr l1) l2))
l2 ) )
On s'aperçoit qu'elle appelle autant de fois
cons que la première liste a de termes. C'est donc toujours
une très mauvaise idée d'utiliser
append pour ajouter un
terme en queue de liste, c'est d'ailleurs toujours une très mauvaise
idée d'ajouter un terme en queue de liste quelque soit la méthode
employée. Les listes sont disymétriques, on ajoute facilement en tête
(avec
cons), on verra également que l'on retire facilement
des informations de la tête d'une liste et que donc l'accès aux listes
par leur tête est toujours conseillé.
Le plus souvent une simple analyse des types des valeurs que l'on
souhaite réunir en liste permet de savoir quel constructeur employer
puisqu'ils ont tous des types distincts.
Petit conseil: n'écrivez jamais
(append (list p) p')
à la place de
(cons p p'), ce serait commettre une
énorme faute de mauvais goût que nous ne pardonnerions point!