Listes
Les listes permettent de stocker un certain nombre d'éléments du même type. Par exemple,
[1,2,3,4,5]
est une liste. Comme vous le voyez, les listes sont notées entre crochets, et les éléments sont séparés par des virgules. Par contre,
[1,2,(3,4)]
n'est pas une liste valide parce que les éléments n'ont pas le même type. Un cas particulier de liste est
[]
, qui représente la liste vide.
Opérations sur les listes
Il existe beaucoup de fonctions pour manipuler les listes et toutes les présenter serait beaucoup trop long. Je ne montrerai que les plus importantes.
Pour prendre deux listes et les mettre bout à bout, on utilise l'opérateur de concaténation
++ :
Code : Console | Prelude> [1,2,3] ++ [4,5,6]
[1,2,3,4,5,6]
Prelude> [1,2] ++ [(3,4)]
<interactive>:1:5:
Couldn't match expected type `Integer'
against inferred type `(Integer, Integer)'
Expected type: [Integer]
Inferred type: [(Integer, Integer)]
In the second argument of `(++)', namely `b'
In the expression: a ++ b |
On obtient une erreur dans le deuxième cas, puisque la liste obtenue aurait des éléments de types différents. Attention, ++ n'est pas très efficace : plus la première liste est longue, plus ++ prend de temps. Cela prend beaucoup de temps de rajouter un élément à la fin d'une longue liste. Au contraire, si on ajoute un élément au début de la liste, quelle que soit la liste, l'opération est instantanée. On pourrait écrire
[0]++[1,2,3]
, mais il existe un opérateur exprès pour ça :
:. Cet opérateur, parfois appelé
cons permet d'ajouter un élément au début de la liste. C'est l'opération de base permettant de construire une liste, toutes les autres opérations qui créent une liste l'utilisent. cons ne copie pas la liste devant laquelle on rajoute un élément, mais puisqu'on ne peut pas la modifier, vous ne vous en rendrez pas compte. Cependant, c'est ça qui lui permet d'être si rapide.
Code : Console | Prelude> 0:[1,2,3]
[0,1,2,3]
Prelude> 0:1:2:3:[]
[0,1,2,3] |
Le deuxième exemple montre que l'on peut toujours écrire une liste à partir de
: et de la liste vide. D'ailleurs, noter une liste entre crochets, comme
[1,2,3]
, est seulement un raccourci pour cette notation.
head et
tail sont les opérations inverses de cons :
head donne le premier élément d'une liste, et
tail la liste à laquelle on a retiré ce premier élément. Comme cons, ces opérations sont instantanées et ne demandent pas de copier la liste.
Code : Console | Prelude> let xs = [0,1,2,3]
Prelude> head xs
0
Prelude> tail xs
[1,2,3]
Prelude> head xs:tail xs
[0,1,2,3]
Prelude> head []
*** Exception: Prelude.head: empty list
Prelude> tail []
*** Exception: Prelude.tail: empty list |
Comme vous le voyez,
head et
tail renvoient une erreur quand la liste est vide, puisqu'une liste vide n'a pas de premier élément.
Si on veut prendre un élément particulier d'une liste, on peut utiliser l'opérateur
!!.
liste !! n
donne l'élément de rang
n de la liste (les éléments sont numérotés à partir de 0). Si la liste n'a pas d'élément de rang
n, on obtient une erreur.
Code : Console | Prelude> [1,2,3] !! 0
1
Prelude> [1,2,3] !! 3
*** Exception: Prelude.(!!): index too large |
Les fonctions
take et
drop permettent respectivement de prendre les n premiers éléments de la liste, et la liste à laquelle on a enlevé les n premiers éléments. Ces fonctions ne renvoient pas d'erreur quand n est trop grand.
Code : Console | Prelude> let xs = [1,2,3,4,5]
Prelude> take 2 xs
[1,2]
Prelude> drop 2 xs
[3,4,5]
Prelude> take 100 xs
[1,2,3,4,5]
Prelude> drop 100 xs
[] |
La fonction
elem permet de tester si un élément est dans une liste ou non. Elle renvoie
True si c'est le cas,
False sinon. On l'utilise souvent en notation infixe.
Code : Console | Prelude> 1 `elem` [0,1,2]
True
Prelude> 42 `elem` [1,3,3,7]
False |
Avec
reverse, il est possible de renverser l'ordre d'une liste.
Code : Console | Prelude> reverse [1,2,3]
[3,2,1] |
length renvoie la longueur d'une liste. Les fonctions
minimum et
maximum renvoient, sans surprise, le minimum et le maximum des éléments d'une liste (à condition qu'on puisse les ordonner). Enfin
sum et
product renvoient respectivement la somme et le produit des éléments d'une liste de nombres. Quelques exemples :
Code : Console | Prelude> let liste = [1,42,47,85,62,31,12,93]
Prelude> length liste
8
Prelude> length []
0
Prelude> maximum liste
93
Prelude> minimum liste
1
Prelude> sum liste
373
Prelude> product liste
359901496080 |
Il est aussi possible de créer des listes de listes. Les listes peuvent avoir des longueurs différentes, mais doivent toutes contenir des éléments du même type. Par exemple,
[[],[]]
est une liste de liste valide, mais
[[5,6],[[]]]
ne marche pas : le premier élément est une liste d'entiers et le deuxième est une liste de listes. On peut transformer une liste de listes en liste tout court avec la fonction
concat :
Code : Console | Prelude> concat [[1,2,3],[4,5,6],[7,8,9]]
[1,2,3,4,5,6,7,8,9] |
Noter une séquence
Dans les exemples précédents, toutes les listes de nombres ont été entrées à la main. Mais si on voulait la liste des nombres de 1 à 100 ? On pourrait les entrer à la main, mais ce serait bien trop long. Heureusement, Haskell offre une syntaxe spéciale pour les suites arithmétiques.
Pour afficher tous les entiers entre deux entiers donnés, il suffit d'écrire entre crochets le premier nombre, puis le dernier nombre et de mettre deux points entre les deux.
Code : Console | Prelude> [0..10]
[0,1,2,3,4,5,6,7,8,9,10]
Prelude> let n = 42
Prelude [n..n+5]
[42,43,44,45,46,47] |
On peut écrire n'importe quelle suite arithmétique en donnant les deux premiers nombres, puis le dernier. On peut aussi utiliser cette notation quand on veut que les nombres soient dans l'ordre décroissant :
Code : Console | Prelude>[0,2..10]
[0,2,4,6,8,10]
Prelude> [10..0]
[]
Prelude> [10,9..0]
[10,9,8,7,6,5,4,3,2,1,0] |
Cependant, cela ne marche qu'avec les suites arithmétiques. Il y a aussi quelques problèmes avec les nombres à virgules, donc il vaut mieux éviter de les utiliser avec cette notation. Ces problèmes ne sont pas causés par le langage en lui-même, mais par la façon dont les nombres à virgule sont représentés en mémoire. Par exemple :
Code : Console | Prelude> [0.1,0.3..1]
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999] |
On peut bien sûr combiner cette notation avec toutes les fonctions sur les listes. Par exemple, pour calculer

(le produit de tous les nombres de 1 à 20), il suffit d'utiliser
product :
Code : Console | Prelude> product [1..20]
2432902008176640000 |
Des listes infinies
Que se passe-t-il si on écrit
[1..]
? Si on essaye, on obtient
Code : Console | Prelude> [1..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,Interrupted. |
J'ai appuyé sur Ctrl-C pour l'arrêter, sinon il allait remplir mon écran de chiffres, mais si je l'avais laissé tourner, il aurait affiché la liste des tous les nombres à partir de 1. On peut donc obtenir des listes infinies en Haskell. On peut les manipuler presque comme des listes normales : on peut prendre certains de leurs éléments, ajouter un élément devant, ... En général, on prend la précaution de mettre quelque chose comme
take 10
avant d'afficher la liste pour éviter les catastrophes.
Code : Console | Prelude> take 10 [1..]
[1,2,3,4,5,6,7,8,9,10]
Prelude> take 10 (0:[1..])
[0,1,2,3,4,5,6,7,8,9] |
Si on peut faire des listes infinies, c'est grâce à l'évaluation paresseuse : un élément de la liste n'est calculé que lorsqu'il est réellement demandé. Cependant, certaines fonctions comme
reverse,
minimum et
maximum,
sum et
product ne se terminent pas sur les listes infinies, car elles ont besoin de lire la liste en entier pour pouvoir répondre.
Quelques autres fonctions permettent de manipuler les listes infinies :
cycle répète une liste une infinité de fois,
repeat répète seulement un élément. La fonction
replicate fait la même chose que repeat, sauf qu'elle prend un argument qui indique combien de fois l'élément doit être répété.
Code : Console | Prelude> take 20 (cycle [0..2])
[0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1]
Prelude> take 20 (repeat 0)
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
Prelude> replicate 5 0
[0,0,0,0,0] |
Chaînes de caractères
Notation
On note une chaîne de caractères entre guillemets doubles. Par exemple,
"Hello World"
(essayez d'entrer ça dans ghci). Pour échapper les caractères gênants (comme
" et
\), on utilise un
\. Exemple :
"Plein de \" et de \\"
.
Ce sont des listes !
En réalité, les chaînes de caractères sont juste des listes de caractères. Un caractère se note entre apostrophes, par exemple
'a'
. On peut aussi utiliser des séquences d'échappement quand on note des caractères :
'\n'
représente un retour à la ligne.
Cela veut dire que l'on peut utiliser toutes les opérations disponibles sur les listes sur des chaînes de caractères :
Code : Console | Prelude> 'a':"bcd"
"abcd"
Prelude> "Hello " ++ "World"
"Hello World"
Prelude> reverse "Hello World"
"dlroW olleH"
Prelude> 'a' `elem` "Hello World"
False |
On peut aussi noter des suites de caractères de la même façon que des suites de nombres :
Code : Console | Prelude> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
Prelude> ['a','c'..'z']
"acegikmoqsuwy"
Prelude> ['z','y'..'a']
"zyxwvutsrqponmlkjihgfedcba" |