1

有没有一种优雅的方法来制作更新字典值的自定义运算符?

更具体地说,我想要一个前缀运算符,它增加与给定键对应的整数值:

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]

但我正在尝试使用自定义运算符做同样的事情

4

3 回答 3

1

首先,寻找一种使用函数(而不是自定义运算符)的方法。您需要一个函数来引用一个项目(来自字典)并更新它的值......这需要一个inout参数类型。

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

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

您不必关心字典中的值 -inout获取任何类型的引用并直接更新它。(这甚至适用于计算属性。你可以传递一个inout,它会在读取和写入值时正确地通过 setter 和 getter。)而且因为你不必关心字典,你真的不需要通用——如果你想要一个在带有Ints 的字典上工作的函数,只需创建一个在Ints上工作的函数,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]"
于 2015-07-30T19:04:03.070 回答
1
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]

虽然我不确定它会如何给你一个频率函数——我的意思是,如果它正在构建一个字典,它不会有任何开始的键,所以不会有任何增加的东西。不过,有很多很酷的方法可以做到这一点。使用 postfix ++,您可以这样做:

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
  }
}
于 2015-07-30T18:47:59.927 回答
0

这比您可能正在寻找的要丑陋一些,但是您可以在泛型重载运算符中使用不安全的可变指针来完成它:

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)
于 2015-07-30T18:54:37.627 回答