Swift : 自定义运算符更新字典值
Swift : Custom operator to update dictionary value
有没有一种优雅的方法来制作更新字典值的自定义运算符?
更具体地说,我想要一个前缀运算符来递增与给定键对应的整数值:
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{[=12=] + 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]
但我正在尝试使用自定义运算符做同样的事情
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
}
}
这可能比您正在寻找的要难看一点,但您可以在通用重载运算符中使用不安全的可变指针来完成它:
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
参数类型。
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。)而且因为您不必须关心字典,你真的不需要是通用的——如果你想要一个在字典上使用 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]"
有没有一种优雅的方法来制作更新字典值的自定义运算符?
更具体地说,我想要一个前缀运算符来递增与给定键对应的整数值:
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{[=12=] + 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]
但我正在尝试使用自定义运算符做同样的事情
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
}
}
这可能比您正在寻找的要难看一点,但您可以在通用重载运算符中使用不安全的可变指针来完成它:
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
参数类型。
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。)而且因为您不必须关心字典,你真的不需要是通用的——如果你想要一个在字典上使用 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]"