J'ai remarqué que la plupart des langages fonctionnels utilisent une liste à liaison unique (une liste "contre") comme types de liste les plus fondamentaux. Les exemples incluent Common Lisp, Haskell et F #. Ceci est différent des langages traditionnels, où les types de liste natifs sont des tableaux.
Pourquoi donc?
Pour Common Lisp (étant typé dynamiquement), j'ai l'idée que les inconvénients sont suffisamment généraux pour être également la base de listes, d'arbres, etc. Cela pourrait être une toute petite raison.
Pour les langages typés statiquement, cependant, je ne trouve pas de bon raisonnement, je peux même trouver des contre-arguments:
- Le style fonctionnel encourage l'immuabilité, la facilité d'insertion de la liste chaînée est donc moins avantageuse,
- Le style fonctionnel encourage l'immuabilité, ainsi que le partage de données; un tableau est plus facile à partager "partiellement" qu'une liste chaînée,
- Vous pouvez aussi bien faire correspondre des motifs sur un tableau régulier, et encore mieux (vous pouvez facilement vous plier de droite à gauche par exemple),
- En plus de cela, vous obtenez un accès aléatoire gratuit,
- Et (un avantage pratique) si la langue est typée statiquement, vous pouvez utiliser une disposition de mémoire régulière et obtenir une augmentation de la vitesse du cache.
Alors pourquoi préférer les listes chaînées?
an array is easier to share "partially" than a linked list
faut clarifier ce que vous voulez dire. En raison de leur nature récursive, l'inverse est vrai si je comprends bien - vous pouvez partager partiellement une liste liée plus facilement en passant le long de n'importe quel nœud, tandis qu'un tableau aurait besoin de passer du temps à faire une nouvelle copie. Ou en termes de partage de données, deux listes liées peuvent pointer vers le même suffixe, ce qui n'est tout simplement pas possible avec les tableaux.Réponses:
Le facteur le plus important est que vous pouvez ajouter à une liste immuable liée individuellement en temps O (1), ce qui vous permet de créer de manière récursive des listes de n éléments en temps O (n) comme ceci:
Si vous faisiez cela en utilisant des tableaux immuables, le temps d'exécution serait quadratique car chaque
cons
opération aurait besoin de copier le tableau entier, ce qui conduirait à un temps d'exécution quadratique.Je suppose qu'en partageant "partiellement", vous voulez dire que vous pouvez prendre un sous-tableau d'un tableau en temps O (1), alors qu'avec les listes liées, vous ne pouvez prendre la queue qu'en O (1) et tout le reste a besoin de O (n). C'est vrai.
Cependant, prendre la queue suffit dans de nombreux cas. Et vous devez tenir compte du fait que pouvoir créer des sous-réseaux à bas prix ne vous aide pas si vous n'avez aucun moyen de créer des tableaux à moindre coût. Et (sans optimisations intelligentes du compilateur), il n'y a aucun moyen de construire pas à pas un tableau pas à pas.
la source
Je pense que cela revient à des listes qui sont assez facilement implémentées dans du code fonctionnel.
Schème:
Haskell:
Les tableaux sont plus difficiles et pas aussi jolis à mettre en œuvre. Si vous voulez qu'ils soient extrêmement rapides, ils devront être écrits en bas niveau.
De plus, la récursivité fonctionne beaucoup mieux sur les listes que sur les tableaux. Considérez le nombre de fois où vous avez consommé / généré récursivement une liste vs indexé un tableau.
la source
Une liste à liaison unique est la structure de données persistante la plus simple .
Les structures de données persistantes sont essentielles pour une programmation performante et purement fonctionnelle.
la source
Vous pouvez utiliser facilement les nœuds Cons uniquement si vous disposez d'une langue récupérée.
Les nœuds correspondent beaucoup au style de programmation fonctionnel des appels récursifs et des valeurs immuables. Il s'intègre donc bien dans le modèle du programmeur mental.
Et n'oubliez pas les raisons historiques. Pourquoi sont-ils encore appelés Cons Nodes et pire encore utilisent-ils la voiture et le cdr comme accessoires? Les gens l'apprennent à partir de manuels et de cours, puis l'utilisent.
Vous avez raison, dans le monde réel, les tableaux sont beaucoup plus faciles à utiliser, ne consomment que la moitié de l'espace mémoire et sont beaucoup plus performants en raison des échecs de niveau de cache. Il n'y a aucune raison de les utiliser avec des langages impératifs.
la source
Les listes liées sont importantes pour la raison suivante:
Une fois que vous avez pris un nombre comme 3, que vous l'avez converti en séquence successive comme
succ(succ(succ(zero)))
, puis que vous y avez utilisé la substitution avec{succ=List node with some memory space}
, et{zero = end of list}
, vous vous retrouverez avec une liste chaînée (de longueur 3).La partie importante réelle est les nombres, la substitution et l'espace mémoire et zéro.
la source