2016-07-13 13 views
10

In Scala gibt der Operator + (k -> v) unter immutable.Map einen neuen immutable.Map mit dem Inhalt des Originals und dem neuen Schlüssel/Wert-Paar zurück. In C# gibt ImmutableDictionary.add(k, v) einen neuen, aktualisierten ImmutableDictionary zurück.Wie kann ich an ein unveränderbares Wörterbuch in Swift "anhängen"?

In Swift jedoch scheint Dictionary nur die mutierende updateValue(v, forKey: k) Funktion und den mutierenden [k:v] Operator zu haben.

Ich dachte, vielleicht ich etwas Trick mit flatten() spielen konnte, aber kein Glück:

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

bringt mich

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

Wie erstelle ich ein neues, Dictionary von den Inhalten ein unveränderlich geändert existierende?


Update: Basierend auf this answer ‚s zur Kenntnis, dass Swift Wörterbücher Werttypen sind, und this answer‘ s wandelbar Version, kam ich auf die folgende Erweiterung Betreiber, aber ich bin darüber nicht begeistert - es scheint, als müsste es eine sauberere Alternative geben.

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

Aber vielleicht die Tatsache, (wenn ich das richtig verstehe), dass die Unveränderlichkeit der Swift Wörterbücher auf let eine Compiler Prüfung eher als eine Frage der unterschiedlichen Implementierungsklassen bedeutet dies das Beste ist, was getan werden kann?


Update # 2: Wie in Jules's answer erwähnt, Modifizieren unveränderliche Wörterbücher, die nicht speziell optimiert sind Zustand zwischen Kopien zu teilen (wie Swift Wörterbücher sind nicht) präsentiert Performance-Probleme. Für meinen aktuellen Anwendungsfall (AttributedString Attributwörterbücher, die eher klein sind) kann es dennoch gewisse Dinge vereinfachen, um es wert zu sein, aber bis Swift ein unveränderbares Wörterbuch mit gemeinsamem Status implementiert, ist es wahrscheinlich keine gute Idee im Allgemeinen Fall - was ein guter Grund ist, es nicht als eingebaute Funktion zu haben.

+1

Sieht aus wie jemand baut eine Bibliothek, um genau das zu tun https://github.com/tLewisII/ImStructures (Disclaimer: Ich habe es nicht überprüft.) –

+0

*, dass die Unveränderlichkeit der Swift-Wörterbücher ist ein Compiler-Check [. ..] verschiedene Implementierungsklassen * Das ist falsch. Wörterbücher werden als Strukturen implementiert, was bedeutet, dass sie (grundsätzlich) von der Kopie übernommen werden. Jeder Strukturwert, der in einer 'let' -Variable gespeichert ist, ist unveränderlich, daher können Sie keine' muting'-Methoden aufrufen, da diese den Wert ändern würden. Wenn Sie ein Dictionary (irgendeine Struktur) einer 'var'-Variable zuweisen, wird es kopiert! Sie haben dann ein anderes Wörterbuch mit einem neuen Speicherort. – idmean

Antwort

5

Leider ist dies eine gute Frage, denn die Antwort lautet: "Sie können nicht". Noch nicht, wie auch immer - andere stimmen zu, dass dies hinzugefügt werden sollte, weil es eine Swift Evolution proposal for this (and some other missing Dictionary features) gibt. Es wartet derzeit auf eine Überprüfung, so dass Sie möglicherweise eine merged() Methode sehen, die im Grunde Ihr + Operator in einer zukünftigen Version von Swift ist!

In der Zwischenzeit können Sie Ihre Lösung anhängen ganze Wörterbücher verwenden, oder zu einem Zeitpunkt einen Wert für:

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

Gute Idee Verknüpfung mit dem Vorschlag, aber 'merge' ist nicht genau das, was OP ist danach. – jtbandes

+1

Nicht genau, aber es ist nah dran an dem, was er mit 'flatten()' versucht hat: es erlaubt 'let newDictionary = oldDictionary.merged ([new: stuff])' ', was gut genug für mich ist. (Lustig genug, es wird auch erlauben, dass sein existierender 'flatten()' Trick funktioniert.) – andyvn22

+0

@jtbandes Es ist nicht so, aber da es genauso knapp ist wie '+ (Tupel: (K, V))' für einzelnen Schlüsselwert Paare, es könnte gut genug sein, besonders wenn die Implementierung klug genug ist, um O (log n) zu sein. –

2

Es gibt keine eingebaute Möglichkeit, dies jetzt zu tun. Sie könnten Ihre eigenen mit einer Erweiterung schreiben (unten).

Aber denken Sie daran, dass dies wahrscheinlich kopieren Sie das Wörterbuch, weil Wörterbücher Copy-on-Write sind, und Sie tun genau das (eine Kopie machen, dann mutieren). Sie können nur mit einem wandelbaren Variable in erster Linie :-)

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

Die einfachste Sache, all dies vermeiden zu tun ist, um eine Variable zu kopieren, modifizieren, dann neu zuweisen zurück auf eine Konstante:

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

Nicht schön, natürlich, aber es könnte leicht in eine Funktion verpackt werden.

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) 

Ich glaube nicht, dass es eine andere Lösung als Roll-your-Own gibt.

Aber vielleicht die Tatsache, (wenn ich das richtig verstehe), dass die Unveränderlichkeit der Swift Wörterbücher auf let

Richtig, ein Compiler Scheck wird Veränderlichkeit auf dem Speicherangegeben, das heißt, die Variable nicht auf dem Wert.

1

Versuchen Sie nicht, eine unveränderliche Wörterbuch zu aktualisieren, es sei denn, es für Unveränderlichkeit speziell entwickelt wurde.

Unveränderliche Wörterbücher verwenden normalerweise eine Datenstruktur (z. B. einen rot/schwarzen Baum mit unveränderlichen Knoten, die von Instanzen oder Ähnlichem gemeinsam genutzt werden können), die eine geänderte Kopie erstellen können, ohne Kopien des gesamten Inhalts erstellen zu müssen Teilmenge (dh sie haben O (log (n)) Kopier- und Änderungsoperationen), aber die meisten Wörterbücher, die für ein veränderbares System entworfen wurden und dann mit einer unveränderlichen Schnittstelle verwendet werden, haben nicht O (n) kopieren und modifizieren Operationen. Wenn Ihr Wörterbuch größer als ein paar hundert Knoten wird, werden Sie den Leistungsunterschied bemerken.

+0

In diesem speziellen Fall sehe ich _n_ «100, aber die Warnung wird notiert. Aus anderen Kommentaren/Antworten klingt es, als ob Swift unveränderliche Wörterbücher genau dieses Problem haben. –

Verwandte Themen