Swift:自定义运算符来更新字典值

Posted

技术标签:

【中文标题】Swift:自定义运算符来更新字典值【英文标题】:Swift : Custom operator to update dictionary value 【发布时间】:2015-07-30 18:21:19 【问题描述】:

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

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

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]

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

【问题讨论】:

【参考方案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]

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

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 tweeted another cool way:

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
  

【讨论】:

是这条推文把我带到了这里。也许只是 xcode beta 4 有一个错误,不知道如何处理 ++ 运算符【参考方案2】:

首先,寻找一种使用函数(而不是自定义运算符)的方法。您需要一个函数,它可以引用一个项目(来自字典)并更新它的值……它需要一个 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。)而且因为您不必关心字典,所以您不需要t 真的需要是通用的——如果你想要一个在 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]"

【讨论】:

您的+&gt;++ 做的事情几乎完全相同,不是吗?我的意思是,++d["second"]! 工作正常。 这种强制解开对我来说是致命的。相反,我会这样做:prefix func +&gt;(inout n: Int?) n?++ @oisdk 是的,但是 OP 显然询问了一般的自定义运算符定义 - 没有说 +&gt; 的主体 必须 执行增量。跨度> 【参考方案3】:

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

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)

【讨论】:

是的,我忘了inout,我也喜欢rickster 的回答。

以上是关于Swift:自定义运算符来更新字典值的主要内容,如果未能解决你的问题,请参考以下文章

swift 在Swift中创建自定义运算符

swift swift中的自定义随机数运算符

swift swift中的自定义整数运算符,允许您连接整数

Swift之深入解析如何自定义操作符

Swift 3、RxAlamofire 和映射到自定义对象

为自定义私有类实现 Equatable - Swift