2016-07-13 83 views
10

In Scala, l'operatore + (k -> v) su immutable.Map restituisce un nuovo immutable.Map con il contenuto dell'originale, più la nuova coppia chiave/valore. Analogamente, in C#, ImmutableDictionary.add(k, v) restituisce un nuovo ImmutableDictionary aggiornato.Come faccio ad "accodare" a un dizionario immutabile in Swift?

In Swift, tuttavia, Dictionary appare solo per la funzione di muting updateValue(v, forKey: k) e l'operatore di muting [k:v].

ho pensato che forse avrei potuto giocare un po 'di trucco con flatten(), ma senza fortuna:

let updated = [original, [newKey: newValue]].flatten() 

mi viene

Cannot convert value of type '() -> FlattenCollection<[[String : AnyObject]]>' 
to specified type '[String : AnyObject]' 

Come faccio a creare una nuova, modificata immutabile Dictionary dai contenuti di un quello esistente?


Aggiornamento: Sulla base di 's nota che Swift dizionari sono tipi di valore, e this answer' this answer versione mutevole s, mi si avvicinò con il seguente operatore estensione, ma io non sono entusiasta - sembra che ci debba essere un'alternativa più pulita e pronta all'uso.

func + <K, V>(left: [K:V], right: [K:V]) -> [K:V] { 
    var union = left 
    for (k, v) in right { 
     union[k] = v 
    } 
    return union 
} 

Ma forse il fatto (se ho capito bene), che l'immutabilità di dizionari Swift è un controllo sulla compilatore let piuttosto che una questione di differenti classi di implementazione significa che questo è il meglio che si può fare?


Aggiornamento # 2: Come osservato in Jules's answer, modificando dizionari immutabili che non sono specificatamente ottimizzate per condividere lo stato tra copie (come Swift dizionari non sono) presenta problemi di prestazioni. Per il mio caso d'uso corrente (i dizionari degli attributi AttributedString, che tendono ad essere piuttosto piccoli) può ancora semplificare alcune cose che valga la pena di essere fatte, ma fino a quando Swift non implementa un dizionario immutabile con stato condiviso probabilmente non è una buona idea in generale case - che è una buona ragione per non averlo come funzionalità integrata.

+1

Sembra che qualcuno stia costruendo una biblioteca per fare proprio questo https://github.com/tLewisII/ImStructures (Disclaimer: non l'ho controllato.) –

+0

* che l'immutabilità dei dizionari Swift è un controllo del compilatore [. ..] diverse classi di implementazione * Questo è sbagliato. I dizionari sono implementati come strutture e ciò significa che sono (fondamentalmente) passati per copia. Qualsiasi valore di struct che è memorizzato in una variabile 'let' è immutabile, quindi non è possibile richiamare i metodi' mutating' su di esso in quanto cambierebbero il valore. Se assegni un dizionario (qualsiasi struttura) ad una variabile 'var', questo viene copiato! Quindi hai un dizionario diverso con una nuova posizione di memoria. – idmean

risposta

5

Sfortunatamente, questa è una buona domanda perché la risposta è "non puoi". Non ancora, comunque - altri concordano che questo dovrebbe essere aggiunto, perché c'è un Swift Evolution proposal for this (and some other missing Dictionary features). Al momento è "in attesa di revisione", quindi potresti vedere un metodo merged() che è fondamentalmente l'operatore + in una versione futura di Swift!

Nel frattempo, è possibile utilizzare la soluzione per aggiungere interi dizionari, o per un valore alla volta:

extension Dictionary { 
    func appending(_ key: Key, _ value: Value) -> [Key: Value] { 
     var result = self 
     result[key] = value 
     return result 
    } 
} 
+0

Buona idea di collegamento alla proposta, ma l'unione non è esattamente ciò che l'OP sta cercando. – jtbandes

+1

Non esattamente, ma è vicino a quello che ha provato con 'flatten()': consentirà 'let newDictionary = oldDictionary.merged ([new: stuff])', che è abbastanza buono per me. (Abbastanza divertente, consentirà anche al suo attuale trucco 'flatten()' di funzionare.) – andyvn22

+0

@jtbandes Non lo è, ma dal momento che è altrettanto conciso come '+ (tupla: (K, V))' per valore chiave singolo coppie, potrebbe essere abbastanza buono, specialmente se l'implementazione è abbastanza intelligente da essere O (log n). –

2

Non esiste un modo integrato per farlo al momento. Puoi scrivere il tuo usando un'estensione (sotto).

Ma tenete a mente che questo probabilmente copia il del dizionario, perché i dizionari sono copia-su-scrittura e lo state facendo esattamente (facendo una copia, quindi modificandola). È possibile evitare tutto questo da solo utilizzando una variabile mutabile in primo luogo :-)

extension Dictionary { 
    func updatingValue(_ value: Value, forKey key: Key) -> [Key: Value] { 
     var result = self 
     result[key] = value 
     return result 
    } 
} 

let d1 = ["a": 1, "b": 2] 
d1 // prints ["b": 2, "a": 1] 
let d2 = d1.updatingValue(3, forKey: "c") 
d1 // still prints ["b": 2, "a": 1] 
d2 // prints ["b": 2, "a": 1, "c": 3] 
1

La cosa più semplice da fare è copiare a una variabile, modificare, poi ri-assegnare di nuovo ad un costante:

var updatable = original 
updatable[newKey] = newValue 
let updated = updatable 

Non bello, ovviamente, ma potrebbe essere facilmente racchiuso in una funzione.

extension Dictionary { 
    func addingValue(_ value: Value, forKey key: Key) -> Dictionary<Key, Value> { 
     // Could add a guard here to enforce add not update, if needed 
     var updatable = self 
     updatable[key] = value 
     return updatable 
    } 
} 

let original = [1 : "One"] 
let updated = original.addingValue("Two", forKey: 2) 

Non credo che ci sia una soluzione oltre a roll-your-own.

Ma forse il fatto (se ho capito bene), che l'immutabilità di dizionari Swift è un controllo compilatore su let

Destra, mutevolezza è specificato sul stoccaggio, vale a dire, la variabile , non sul valore .

1

Non cercare di aggiornare un dizionario immutabile, se non è stato specificamente progettato per l'immutabilità.

I dizionari immutabili usano solitamente una struttura dati (come un albero rosso/nero con nodi immutabili che possono essere condivisi tra istanze o simili) che può generare una copia modificata senza dover fare copie dell'intero contenuto, ma solo un sottoinsieme (cioè hanno O (log (n)) operazioni di copia e modifica) ma la maggior parte dei dizionari progettati per un sistema mutabile e quindi usati con un'interfaccia immutabile no, così hanno O (n) copia e modifica operazioni. Quando il tuo dizionario inizia a diventare più grande di qualche centinaio di nodi, noterai davvero la differenza di prestazioni.

+0

In questo caso particolare, sto guardando _n_ «100, ma l'avviso è annotato. Da altri commenti/risposte sembra che i dizionari immutabili di Swift abbiano esattamente questo problema. –

Problemi correlati