Vorrei imparare come creare grafici ed eseguire alcune operazioni locali su di essi in Haskell, ma la domanda non è specifica per Haskell e al posto dei grafici possiamo prendere in considerazione elenchi doppiamente collegati.
Domanda: quale sarebbe un modo idiomatico o raccomandato per implementare un elenco doppiamente collegato (o altre strutture di dati doppiamente collegate o circolari) e operazioni su di esso in un linguaggio che supporta e sostiene principalmente strutture di dati immutabili (Haskell, Clojure, ecc.) ? In particolare, come utilizzare gli aggiornamenti sul posto, che sono formalmente vietati dalla lingua?
Posso facilmente immaginare che se un'operazione locale viene eseguita su un elenco doppiamente collegato (se viene inserito un elemento, ad esempio), potrebbe non essere necessario copiare l'intero elenco immediatamente a causa della pigrizia della lingua. Tuttavia, poiché l'elenco è doppiamente collegato, se viene modificato in un unico punto, nessuno dei vecchi nodi può essere utilizzato nella nuova versione dell'elenco e dovrebbero essere in qualche modo contrassegnati, copiati, immondizia prima o poi . Ovviamente si tratta di operazioni ridondanti se si utilizza solo la copia aggiornata dell'elenco, ma aggiungerebbe un "overhead" proporzionale alla dimensione dell'elenco.
Ciò significa che per tali compiti i dati immutabili sono semplicemente inappropriati e che i linguaggi dichiarativi funzionali senza supporto "nativo" per i dati mutabili non sono buoni come quelli imperativi? Oppure, c'è qualche soluzione complicata?
PS Ho trovato alcuni articoli e presentazioni su questo argomento su Internet ma ho avuto difficoltà a seguirli, mentre penso che la risposta a questa domanda non dovrebbe richiedere più di un paragrafo e forse un diagramma ... Voglio dire, se c'è nessuna soluzione "funzionale" a questo problema, la risposta probabilmente è "usa C". Se ce n'è uno, allora quanto può essere complicato?
Domande correlate
"Strutture di dati nella programmazione funzionale" . La mia domanda specifica sull'utilizzo degli aggiornamenti sul posto anziché delle alternative inefficienti non è discussa qui.
"Mutazione interna delle strutture di dati persistenti" . Lì l'accento sembra essere posto sull'implementazione di basso livello in un linguaggio non specificato, mentre la mia domanda riguarda la scelta giusta di un linguaggio (funzionale o meno) e le possibili soluzioni idiomatiche nei linguaggi funzionali.
Citazioni pertinenti
I linguaggi di programmazione puramente funzionali consentono a molti algoritmi di essere espressi in modo molto conciso, ma ci sono alcuni algoritmi in cui lo stato aggiornabile sul posto sembra svolgere un ruolo cruciale. Per questi algoritmi, i linguaggi puramente funzionali, privi di stato aggiornabile, sembrano intrinsecamente inefficienti ( [Ponder, McGeer e Ng, 1988] ).
- John Launchbury e Simon Peyton Jones, Lazy funzionale state thread (1994), anche John Launchbury e Simon Peyton Jones, State in Haskell (1995). Questi articoli introducono il ST
costruttore di tipo monadico in Haskell.
DiffArray
tipo. Osservando la fonte del pacchetto diffarray , vedo 91 occorrenze di unsafePerformIO
. Sembra che la risposta alla mia domanda sia "sì, no, i linguaggi puramente funzionali con dati immutabili non sono appropriati per l'implementazione di algoritmi che normalmente si basano su aggiornamenti sul posto".
Map
, IntMap
, o HashMap
) come deposito e per rendere i nodi contengono gli ID dei nodi collegati. "Tutti i problemi di informatica possono essere risolti da un altro livello di riferimento indiretto."