Per chiarire, quando intendo usare i termini persistente e immutable su una struttura dati, voglio dire che:
- Lo stato della struttura dei dati rimane invariato per tutta la sua durata. Ha sempre gli stessi dati e le stesse operazioni producono sempre gli stessi risultati.
- La struttura dei dati consente
Add
,Remove
e metodi simili che restituiscono nuovi oggetti di questo tipo, modificati come da istruzioni, che possono o meno condividere alcuni dei dati dell'oggetto originale.
Tuttavia, mentre una struttura dati può sembrare all'utente persistente, potrebbe fare altre cose sotto il cofano. A dire il vero, tutte le strutture di dati sono, internamente, almeno da qualche parte, basate sulla memoria mutevole.
Se dovessi basare un vettore persistente su un array e copiarlo ogni volta che viene richiamato Add
, sarebbe comunque persistente, purché modifico solo array creati localmente.
Tuttavia, a volte, è possibile aumentare notevolmente le prestazioni modificando una struttura dati sotto il cofano. In più, per esempio, modi insidiosi, pericolosi e distruttivi. Modi che potrebbero lasciare intatto l'astrazione, non lasciare che l'utente sappia nulla è cambiato sulla struttura dei dati, ma essere critico nel livello di implementazione.
Ad esempio, supponiamo di avere una classe chiamata ArrayVector
implementata utilizzando un array. Ogni volta che invochi Add
, ottieni una build ArrayVector
su un array appena assegnato con un elemento aggiuntivo. Una sequenza di tali aggiornamenti coinvolgerà n
di copie e allocazioni di array.
Ecco un'illustrazione:
Tuttavia,supponiamodiimplementareunmeccanismopigrochememorizzatuttiitipidiaggiornamenti,adesempioAdd
,Set
ealtriinunacoda.Inquestocaso,ogniaggiornamentorichiedeuntempocostante(aggiuntadiunelementoaunacoda)enonècoinvoltaalcunaassegnazionediarray.
Quandounutentetentadiottenereunelementonell'array,tuttelemodificheincodavengonoapplicatesottoilcofano,richiedendounasingolaallocazioneecopiadiunarray(poichésappiamoesattamentequalidatisarannopresentinell'arrayfinaleequantosaràgrandeessere).Lefutureoperazionidiacquisizioneverrannoeseguitesuunacachevuota,quindieseguirannoun'unicaoperazione.Maperimplementarlo,dobbiamo"passare" o mutare l'array interno a quello nuovo e svuotare la cache: un'azione molto pericolosa.
Tuttavia, considerando che in molte circostanze (la maggior parte degli aggiornamenti si verificherà in sequenza, dopo tutto), questo può far risparmiare molto tempo e memoria, potrebbe valerne la pena - sarà necessario garantire l'accesso esclusivo al stato interno, naturalmente.
Questa non è una domanda sull'efficacia di una tale struttura di dati. È una domanda più generale. È mai accettabile mutare lo stato interno di un presunto oggetto persistente o immutabile in modi distruttivi e pericolosi? Le prestazioni lo giustificano? Saresti ancora in grado di chiamarlo immutabile?
Oh, potresti implementare questo tipo di pigrizia senza mutando la struttura dei dati nella moda specificata?