Eh bien, si vous regardez un peu plus en profondeur, les deux incluent également des tableaux dans le langage de base:
- Le 5e rapport de schéma révisé (R5RS) inclut le type de vecteur , qui sont des collections indexées sur des nombres entiers de taille fixe avec un temps meilleur que linéaire pour un accès aléatoire.
- Le rapport Haskell 98 a également un type de tableau .
Cependant, l'instruction de programmation fonctionnelle a longtemps mis l'accent sur les listes à simple lien plutôt que sur les tableaux ou les listes à double lien. En fait, il est fort probable que ce soit trop souligné. Il y a cependant plusieurs raisons à cela.
La première est que les listes à lien unique sont l'un des types de données récursives les plus simples et les plus utiles. Un équivalent défini par l'utilisateur du type de liste de Haskell peut être défini comme ceci:
data List a -- A list with element type `a`...
= Empty -- is either the empty list...
| Cell a (List a) -- or a pair with an `a` and the rest of the list.
Le fait que les listes soient un type de données récursif signifie que les fonctions qui fonctionnent sur les listes utilisent généralement la récursivité structurelle . En termes Haskell: vous réglez la correspondance sur les constructeurs de liste, et vous récursivement sur une sous - partie de la liste. Dans ces deux définitions de fonction de base, j'utilise la variable as
pour faire référence à la fin de la liste. Notez donc que les appels récursifs "descendent" dans la liste:
map :: (a -> b) -> List a -> List b
map f Empty = Empty
map f (Cell a as) = Cell (f a) (map f as)
filter :: (a -> Bool) -> List a -> List a
filter p Empty = Empty
filter p (Cell a as)
| p a = Cell a (filter p as)
| otherwise = filter p as
Cette technique garantit que votre fonction se terminera pour toutes les listes finies, et est également une bonne technique de résolution de problèmes - elle a tendance à diviser naturellement les problèmes en sous-parties plus simples et plus tenables.
Les listes à liaison unique sont donc probablement le meilleur type de données pour initier les étudiants à ces techniques, qui sont très importantes en programmation fonctionnelle.
La deuxième raison est moins une raison «pourquoi des listes à liaison unique», mais plutôt une raison «pourquoi pas des listes ou tableaux à double liaison»: ces derniers types de données appellent souvent mutation (variables modifiables), programmation fonctionnelle très souvent fuit. Alors comme ça arrive:
- Dans un langage passionné comme Scheme, vous ne pouvez pas créer une liste à double lien sans utiliser de mutation.
- Dans un langage paresseux comme Haskell, vous pouvez créer une liste à double liaison sans utiliser de mutation. Mais chaque fois que vous créez une nouvelle liste basée sur celle-ci, vous êtes obligé de copier la plupart sinon la totalité de la structure de l'original. Alors qu'avec les listes à lien unique, vous pouvez écrire des fonctions qui utilisent le «partage de structure» - les nouvelles listes peuvent réutiliser les cellules des anciennes listes lorsque cela est approprié.
- Traditionnellement, si vous utilisiez des tableaux de manière immuable, cela signifiait qu'à chaque fois que vous vouliez modifier le tableau, vous deviez copier le tout. (Les bibliothèques Haskell récentes comme
vector
, cependant, ont trouvé des techniques qui améliorent considérablement ce problème).
La troisième et dernière raison s'applique principalement aux langages paresseux comme Haskell: dans la pratique, les listes à liaison unique paresseuses sont souvent plus similaires aux itérateurs qu'aux listes en mémoire proprement dites. Si votre code consomme les éléments d'une liste de façon séquentielle et les jette au fur et à mesure, le code objet ne matérialise que les cellules de la liste et son contenu lorsque vous avancez dans la liste.
Cela signifie que la liste entière n'a pas besoin d'exister en mémoire à la fois, seulement la cellule actuelle. Les cellules avant celle en cours peuvent être récupérées (ce qui ne serait pas possible avec une liste à double liaison); les cellules postérieures à la cellule actuelle n'ont pas besoin d'être calculées tant que vous n'y êtes pas.
Cela va encore plus loin. Il existe une technique utilisée dans plusieurs bibliothèques Haskell populaires, appelée fusion , où le compilateur analyse votre code de traitement de liste et repère les listes intermédiaires qui sont générées et consommées séquentiellement puis «jetées». Avec cette connaissance, le compilateur peut éliminer complètement l'allocation de mémoire des cellules de ces listes. Cela signifie qu'une liste à liaison unique dans un programme source Haskell, après compilation, pourrait en fait devenir une boucle au lieu d'une structure de données.
La fusion est également la technique utilisée par la vector
bibliothèque susmentionnée pour générer du code efficace pour des tableaux immuables. Il en va de même pour les bibliothèques extrêmement populaires bytestring
(tableaux d'octets) et text
(chaînes Unicode), qui ont été construites en remplacement du String
type natif pas très génial de Haskell (qui est le même que la [Char]
liste de caractères à lien unique). Donc, dans Haskell moderne, il existe une tendance où les types de tableaux immuables avec prise en charge de la fusion deviennent très courants.
La fusion de listes est facilitée par le fait que dans une liste à lien unique, vous pouvez avancer mais jamais reculer . Cela soulève un thème très important dans la programmation fonctionnelle: utiliser la "forme" d'un type de données pour dériver la "forme" d'un calcul. Si vous souhaitez traiter les éléments de manière séquentielle, une liste à liaison unique est un type de données qui, lorsque vous le consommez avec une récursivité structurelle, vous donne ce modèle d'accès très naturellement. Si vous souhaitez utiliser une stratégie "diviser pour mieux régner" pour attaquer un problème, les structures de données arborescentes ont tendance à très bien le prendre en charge.
Beaucoup de gens abandonnent le chariot de programmation fonctionnelle dès le début, ils sont donc exposés aux listes à lien unique, mais pas aux idées sous-jacentes plus avancées.