2016-07-13 16 views
10

W Scala operator + (k -> v) pod numerem immutable.Map zwraca nową wartość immutable.Map wraz z zawartością oryginału oraz nową parą klucz/wartość. Podobnie w C#, ImmutableDictionary.add(k, v) zwraca nową, zaktualizowaną ImmutableDictionary.Jak mogę "dołączyć" do niezmiennego słownika w Swift?

Jednak w języku Swift, Dictionary wydaje się mieć tylko funkcję mutowania updateValue(v, forKey: k) i mutujący operator [k:v].

pomyślałem, że może mógłbym zagrać jakąś sztuczkę z flatten(), ale bez powodzenia:

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

dostaje mi

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

Jak utworzyć nowy, zmodyfikowany niezmienne Dictionary z treści istniejący?


Aktualizacja: podstawie „s notatki że słowniki Swift są typy wartości, a this answerthis answer zmienny wersji s, wpadłem na następujący operatora wewnętrznego, ale nie jestem podekscytowany - wygląda na to, że musi istnieć czystsza, gotowa alternatywa.

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

Ale może fakt (jeśli dobrze rozumiem), że niezmienność słowników Swift jest sprawdzenie kompilator na let aniżeli kwestia różnych klas realizacji Oznacza to najlepsze, co można zrobić?


Aktualizacja # 2: Jak zauważono w Jules's answer, modyfikując niezmienne słowniki, które nie są specjalnie zoptymalizowane do dzielenia stan między kopiami (jak słowniki Swift nie są) stwarza problemy z wydajnością. Dla mojego obecnego przypadku użycia (AttributedString słowniki atrybutów, które wydają się być dość małe) może jeszcze uprościć pewne rzeczy na tyle warte, aby być warte zrobienia, ale dopóki Swift nie wdroży niezmiennego słownika dla stanu wspólnego, prawdopodobnie nie jest to dobry pomysł w ogólnym przypadku - co jest dobrym powodem, aby nie mieć go jako wbudowanej funkcji.

+1

Wygląda jak ktoś buduje bibliotekę właśnie do tego https://github.com/tLewisII/ImStructures (Zastrzeżenie:. I nie sprawdzone go) –

+0

* że niezmienność Swift słowników jest sprawdzenie kompilator [. ..] różne klasy implementacji * To jest złe. Słowniki są implementowane jako structs, co oznacza, że ​​są (w zasadzie) przekazywane przez kopie. Każda wartość struct zapisana w zmiennej 'let' jest niezmienna, dlatego nie można na niej wywoływać metod' mutating', ponieważ mogłyby one zmienić wartość. Jeśli przypiszesz słownik (dowolną strukturę) do zmiennej 'var', zostanie on skopiowany! Następnie masz inny słownik z nową lokalizacją w pamięci. – idmean

Odpowiedz

5

Niestety, jest to dobre pytanie, ponieważ odpowiedź brzmi "nie możesz". W każdym razie jeszcze nie - inni zgadzają się, że to powinno być dodane, ponieważ istnieje Swift Evolution proposal for this (and some other missing Dictionary features). Obecnie oczekuje "na sprawdzenie", więc możesz zobaczyć metodę merged(), która jest w zasadzie operatorem + w przyszłej wersji Swift!

W międzyczasie można użyć roztworu do dołączania całe słowniki, lub dla jednej wartości na raz:

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

Dobry pomysł na powiązanie z propozycją, ale 'scalenie' nie jest dokładnie tym, co PO jest po. – jtbandes

+1

Niezupełnie, ale jest bliski tego, co próbował z 'flatten()': to pozwoli 'let newDictionary = oldDictionary.merged ([new: stuff])', co jest wystarczająco dobre dla mnie. (Co zabawne, pozwoli to również na działanie istniejącej sztuczki 'flatten()'.) – andyvn22

+0

@jtbandes Nie jest, ale ponieważ jest tak samo zwięzłe, jak '+ (tuple: (K, V)) dla pojedynczej pary klucz-wartość pary, może to być wystarczająco dobre, szczególnie jeśli implementacja jest wystarczająco inteligentna, aby być O (log n). –

2

Nie ma wbudowanego sposobu, aby to zrobić teraz. Możesz napisać własną przy użyciu rozszerzenia (poniżej).

Należy jednak pamiętać, że prawdopodobnie będzie to kopiować słownik, ponieważ słowniki są kopiowane przy pisaniu, a robisz dokładnie to (tworzenie kopii, a następnie mutowanie). Można uniknąć tego wszystkiego po prostu przy użyciu zmiennej zmienny w pierwszej kolejności :-)

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

Najprostszym rozwiązaniem jest skopiować do zmiennej, zmodyfikować, a następnie ponownie przypisać powrotem do stałej:

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

Nie całkiem, oczywiście, ale można go łatwo zawinąć w funkcję.

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) 

Nie sądzę, że istnieje rozwiązanie inne niż samodzielne.

Ale może fakt (jeśli dobrze rozumiem), że niezmienność Swift słowników jest sprawdzenie kompilator na let

Prawo, zmienność jest określony na przechowywania, czyli zmiennej , a nie na wartości :.

1

Nie próbuj aktualizować niezmienny słownika, chyba że został specjalnie zaprojektowany dla niezmienności.

Niezmienne słowniki zazwyczaj wykorzystują strukturę danych (np. Czerwone/czarne drzewo z niezmiennymi węzłami, które mogą być współdzielone między instancjami lub podobnymi), które mogą generować zmodyfikowaną kopię bez potrzeby wykonywania kopii całej zawartości, a jedynie podzbiór (tzn. mają operacje kopiowania i modyfikacji O (log (n)), ale większość słowników zaprojektowanych dla systemu zmiennego, a następnie używanych z niezmiennym interfejsem, nie ma, więc O (n) kopiuje i modyfikuje operacje. Kiedy twój słownik zacznie się powiększać o kilkaset węzłów, zauważysz różnicę wydajności.

+0

W tym szczególnym przypadku szukam _n_ «100, ale ostrzeżenie jest odnotowywane. Z innych komentarzy/odpowiedzi brzmi to tak, jakby niezmienne słowniki Swift miały dokładnie ten problem. –

Powiązane problemy