comment implémenter des listes doublement liées

Est-il possible d'avoir une liste doublement liée dans Haskell, et quelle est la solution idéale pour les implémenter? J'implémente un graphique de scène où chaque widget a un parent ainsi qu'un enfant, et il est bénéfique de regarder à la fois le haut et le bas du graphique.

25
demandé sur Matt Fenwick 2012-04-30 19:52:22

2 réponses

Il n'est pas vraiment pratique d'avoir une liste doublement liée dans Haskell, car vous devez tout construire en même temps.

Par exemple, imaginez que vous avez une liste [1, 2, 3, 4, 5] que vous voulez faire doublement chaînée. Maintenant, imaginons comment la liste est représentée:

data DoubleList a
  = LeftEnd  a (DoubleList a)
  | Middle   a (DoubleList a) (DoubleList a)
  | RightEnd a (DoubleList a)

(j'utilise deux différents constructeurs pour les deux extrémités pour plus de simplicité)

Pour construire la liste ci-dessus, vous devez d'abord construire le premier élément:

let e1 = LeftEnd  1 ...

Mais pour construire le premier élément, vous avez déjà besoin d'avoir le deuxième élément:

let e1 = LeftEnd  1 e2
    e2 = Middle   2 e1 ...

Et pour le deuxième élément, vous avez besoin du troisième, etc:

let e1 = LeftEnd  1 e2
    e2 = Middle   2 e1 e3
    e3 = Middle   3 e2 e4
    e4 = Middle   4 e3 e5
    e5 = RightEnd 5 e4

C'est possible dans Haskell en raison d'une évaluation paresseuse; cette stratégie s'appelle " attacher le noeud "(et vous n'avez pas à littéralement tout mettre en un seul bloc let; vous pouvez diviser la construction en fonctions)

Mais, en d'autres termes, pour faire une liste doublement liée, vous devez tout construire en même temps, et si jamais vous voulez en changer une partie, vous devez soit pour utiliser une fermeture à glissière ou simplement en faire une copie complète à chaque fois.

Je recommande d'utiliser à la place Data.Sequence, qui est une implémentation de stockage séquentiel basée sur l'arbre des doigts optimisée. Il prend en charge l'insertion, la suppression et l'itération très rapides tout en étant une structure de données purement fonctionnelle.

Sinon, vous voudrez peut-être simplement utiliser des fermetures à glissière, mais les utiliser pour les arbres au lieu de pour les listes. Plus d'informations sur les fermetures à glissière peuvent être trouvées sur le Haskell Wiki. Les fermetures à glissière s'adapteraient très bien dans cette situation, car elles offrent la fonctionnalité exacte que vous recherchez: si vous visitez un arbre avec une fermeture à glissière, vous accédez aux "parents" de la partie de l'arbre que vous visitez, mais l'arbre lui-même n'a pas à contenir les références parentes.

38
répondu dflemstr 2012-04-30 16:10:47

Puisque vous n'avez pas (habituellement) d'objets de style OO dans Haskell, il est étrange de penser que les données sont conscientes de soi. Il est important de noter que vous n'utilisez généralement pas l'agrégation dans les types de données Haskell, en privilégiant la composition à la place.

Vous pouvez regarder dans XMonad pour voir si leur conception correspond à vos besoins (le code est étonnamment lisible).

Vous pouvez également vouloir restructurer votre conception de sorte que vous n'ayez jamais besoin de regarder au-dessus de vous (par exemple, en passant les enfants de "rappels").

Vous pouvez également voir si vous pouvez écrire une fermeture à glissière pour l'ensemble de votre graphique.

1
répondu Alex R 2012-05-10 03:35:47