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 真的需要是通用的——如果你想要一个在 Int
s 的字典上工作的函数,只需创建一个在 Int
s 上工作的函数,然后让 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]"
【讨论】:
您的+>
与++
做的事情几乎完全相同,不是吗?我的意思是,++d["second"]!
工作正常。
这种强制解开对我来说是致命的。相反,我会这样做:prefix func +>(inout n: Int?) n?++
@oisdk 是的,但是 OP 显然询问了一般的自定义运算符定义 - 没有说 +>
的主体 必须 执行增量。跨度>
【参考方案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:自定义运算符来更新字典值的主要内容,如果未能解决你的问题,请参考以下文章