2012-04-30 13 views
25

È possibile avere una lista doppiamente collegata in Haskell e qual è la soluzione ideale per implementarle? Sto implementando un grafico delle scene in cui ogni widget ha un genitore e un figlio, ed è vantaggioso esaminare sia il grafico che il grafico.come implementare liste doppiamente collegate

+1

Mi spiace essere pedante, ma IMHO che chiama le liste di haskell "elenchi concatenati" o che parla di "elenchi collegati doppiamente" nel contesto di pura FP sta trascinando un sacco di bagaglio imperativo (puntatori, aggiornamenti distruttivi) nella discussione e confonde cose. – jberryman

+0

Pertinente al tuo obiettivo se non direttamente alla tua domanda ... C'è un grafico di scena implementato nel documento "Dati enormi ma piccoli programmi" http://eprints.whiterose.ac.uk/5000/ - il codice dovrebbe sperare ancora essere pubblicamente disponibile anche se non è mai stato messo su Hackage. Purtroppo non sembra esserci molto lavoro pubblicato che copre questo dominio con la programmazione funzionale, è un peccato perché è un grande argomento. –

+3

Piuttosto che usare una lista doppiamente collegata, in haskell è molto più semplice archiviare i dati come un albero collegato singolarmente e quindi attraversarli con una [zipper] (http://learnyouahaskell.com/zippers) – rampion

risposta

38

Non è molto pratico avere una lista doppiamente collegata in Haskell, perché devi costruirlo tutto in una volta.

Ad esempio, immagina di avere un elenco [1, 2, 3, 4, 5] che desideri rendere doppiamente collegato. Ora, immaginiamo come la lista è rappresentata:

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

(io uso due costruttori diversi per le due estremità per semplicità)

Per costruire l'elenco di cui sopra, si deve costruire prima il primo elemento:

let e1 = LeftEnd 1 ... 

Ma per costruire il primo elemento, è già bisogno di avere il secondo elemento:

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

E per il secondo elemento, è necessario il terzo, ecc:

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

Questo è possibile fare in Haskell grazie alla valutazione pigra; questa strategia è chiamata "legare il nodo" (E non devi letteralmente metterlo tutto in un blocco let, puoi dividere la costruzione in funzioni)

Ma, in altre parole, fare un doppio - elenco collegato, è necessario costruirlo tutto in una volta e, se si desidera modificare qualsiasi parte di esso, è necessario utilizzare uno Zipper o semplicemente effettuare una copia completa di esso ogni volta.

Si consiglia di utilizzare invece Data.Sequence, che è un'implementazione di archiviazione sequenziale basata su albero dito ottimizzata. Supporta l'inserimento, la cancellazione e l'iterazione molto veloci pur rimanendo una struttura dati puramente funzionale.

In caso contrario, è possibile utilizzare solo le cerniere, ma utilizzarle per gli alberi anziché per gli elenchi. Ulteriori informazioni su Zippers sono disponibili su Haskell Wiki. Le cerniere si adatterebbero molto bene in questa situazione, perché offrono la funzionalità esatta che stai cercando: se visiti un albero con una chiusura lampo, ottieni l'accesso ai "genitori" della parte dell'albero che stai visitando, ma l'albero stesso non deve contenere i riferimenti genitore.

+0

Come per la costruzione di collegamenti doppiamente lista dalla lista ('[a]'), [qui] (http://rosettacode.org/wiki/Doubly-Linked_List_%28traversal%29#Haskell) 'snippet di codice; è la funzione 'create'. – Vitus

+0

bello per Data.Sequence! Ora c'è un tipo interessante! Anche un rapido accesso ad entrambe le estremità della lista che era esattamente quello che stavo cercando –

1

Dato che in Haskell non si hanno (di solito) oggetti in stile OO, è strano pensare che i dati siano auto-consapevoli. È importante notare che di solito non si fa uso dell'aggregazione nei tipi di dati Haskell, preferendo invece la composizione.

Si potrebbe voler esaminare XMonad per vedere se il loro design corrisponde alle proprie esigenze (il codice è sorprendentemente leggibile).

Si potrebbe anche voler ristrutturare il progetto in modo da non dover mai guardare in alto (ad esempio, passando "callback" per bambini).

Si potrebbe anche voler vedere se è possibile scrivere una cerniera per l'intero grafico.

Problemi correlati