Swift: пользовательский оператор для обновления значения словаря

Есть ли элегантный способ сделать пользовательский оператор, который обновляет значение словаря?

Более конкретно, я хочу префиксный оператор, который увеличивает целочисленное значение, соответствующее данному ключу:

prefix operator +> {}

prefix func +> //Signature 
{
    ...
}

var d = ["first" : 10 , "second" : 33]
+>d["second"] // should update d to ["first" : 10 , "second" : 34]

Это возможно с использованием функционального способа. Например, чтобы вычислить частоты элементов в массиве:

func update<K,V>(var dictionary: [K:V], key: K, value: V) -> [K:V] {
    dictionary[key] = value
    return dictionary
}

func increment<T>(dictionary: [T:Int], key: T) -> [T:Int] {
    return update(dictionary, key: key, value: dictionary[key].map{$0 + 1} ?? 1)
}

func histogram<T>( s: [T]) -> [T:Int] {
    return s.reduce([T:Int](), combine: increment)
}

let foo = histogram([1,4,3,1,4,1,1,2,3]) // [2: 1, 3: 2, 1: 4, 4: 2]

Но я пытаюсь сделать то же самое, используя пользовательский оператор

3 ответа

Решение
var d = ["first" : 10 , "second" : 33]

d["second"]?++

Оператор может быть реализован так:

prefix operator +> {}
prefix func +> <I : ForwardIndexType>(inout i: I?) {
  i?._successorInPlace()
}

var dict = ["a":1, "b":2]

+>dict["b"]

dict // ["b": 3, "a": 1]

Хотя я не уверен, как он даст вам функцию частот - я имею в виду, что если он строит словарь, у него не будет никаких ключей для начала, поэтому не будет ничего увеличивать. Однако есть множество классных способов сделать это. Использование постфикса ++, вы можете сделать это:

extension SequenceType where Generator.Element : Hashable {
  func frequencies() -> [Generator.Element:Int] {
    var result: [Generator.Element:Int] = [:]
    for element in self {
      result[element]?++ ?? {result.updateValue(1, forKey: element)}()
    }
    return result
  }
}

Airspeed Velocity написал еще один крутой способ:

extension Dictionary {
  subscript(key: Key, or or: Value) -> Value {
    get { return self[key] ?? or }
    set { self[key] = newValue }
  }
}

extension SequenceType where Generator.Element : Hashable {
  func frequencies() -> [Generator.Element:Int] {
    var result: [Generator.Element:Int] = [:]
    for element in self { ++result[element, or: 0] }
    return result
  }
}

Или, используя недокументированную функцию:

extension SequenceType where Generator.Element : Hashable {
  func frequencies() -> [Generator.Element:Int] {
    var result: [Generator.Element:Int] = [:]
    for el in self {result[el]?._successorInPlace() ?? {result[el] = 1}()}
    return result
  }
}

Во-первых, ищите способ сделать это с помощью функций (не пользовательских операторов). Вам нужна функция, которая берет ссылку на элемент (из словаря) и обновляет его значение... которая вызывает inout тип параметра.

func increment(inout n: Int) {
    n++
}

var d = ["first" : 10 , "second" : 33]
increment(&d["first"]!)
print(d) // -> "[first: 11, second: 33]"

Вам не нужно заботиться о значении в словаре - inout берет любые ссылки и обновляет их напрямую. (Это даже касается вычисленных свойств. Вы можете передать один inout и он будет правильно проходить через установщик и получатель при чтении и записи значений.) И поскольку вам не нужно заботиться о словаре, вам не нужно быть универсальным - если вы хотите функцию, которая работает на словари с Ints, просто сделайте функцию, которая работает на Intи пусть inout сделай все остальное

Теперь пользовательские операторы - это просто функции, поэтому сделайте оператор своей функции:

prefix operator +> {}
prefix func +>(inout n: Int) {
    n++
}

Вы не можете использовать именно тот синтаксис, который вы запрашивали, чтобы вызвать его, хотя: поиск по словарю всегда приводит к необязательным типам, поэтому вам нужно развернуть.

+>d["second"]  // error
+>d["second"]! // but this works — operators automatically make params inout as needed
print(d) // -> "[first: 11, second: 34]"

Это немного страшнее, чем вы, вероятно, ищете, но вы можете сделать это, используя небезопасный изменяемый указатель в универсальном перегруженном операторе:

prefix operator +> {}

prefix func +><T>( value:UnsafeMutablePointer<T?> )
{
    print( value.memory )
    if let intValue = value.memory as? Int {
        value.memory = (intValue + 1) as? T
    }
}

var d = ["first" : 10 , "second" : 33]
print( d["second"] ) // Optional(33)
+>(&d["second"])
print( d["second"] ) // Optional(34)
Другие вопросы по тегам