- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我在 Swift 4 中写了这个效用函数:
func insert<Key, Element>(_ value: Element, into dictionary: inout [Key : Set<Element>], at key: Key) {
if let _ = dictionary[key] {
dictionary[key]?.insert(value)
}
else {
var set = Set<Element>()
set.insert(value)
dictionary[key] = set
}
}
这是这样使用的:
insert("foo", into: &myDictionary, at: "bar")
...但我想这样使用它:
myDictionary.insert("foo", at: "bar")
我试过这样声明:
extension Dictionary where Value == Set<AnyHashable> {
mutating func insert(_ value: Value.Element, at key: Key) { // Error here
if let _ = self[key] {
self[key]?.insert(value)
} else {
var set = Set<Value.Element>() // Error here
set.insert(value)
self[key] = set
}
}
}
...但我收到以下错误:
/path/to/Sequence Extensions.swift:2:41: error: 'Element' is not a member type of 'Dictionary.Value'
mutating func insert(_ value: Value.Element, at key: Key) {
~~~~~ ^
Swift.Set:608:22: note: did you mean 'Element'?
public typealias Element = Element
^
Swift._IndexableBase:3:22: note: did you mean '_Element'?
public typealias _Element = Self.Element
/path/to/Sequence Extensions.swift:6:23: error: type 'Value.Element' does not conform to protocol 'Hashable'
var set = Set<Value.Element>()
^
最佳答案
不幸的是,Swift 目前不支持 parameterised extensions (在扩展声明中引入类型变量的能力),所以你目前不能直接表达“对某些 Set<T>
有约束的扩展”的概念。但是,它是泛型声明的一部分,因此希望它能进入该语言的 future 版本。
即使您的扩展名为 Value
限于 Set<AnyHashable>
编译,它不会非常有用。您需要先将所需字典转换为临时字典 [Key: Set<AnyHashable>]
,然后对其调用 mutating 方法,然后将其转换回其原始类型(使用 as!
)。
这是因为扩展程序位于 Dictionary
上异质Set
值。扩展方法插入任意是完全合法的Hashable
元素到字典的值之一。但这不是你想表达的。
在简单的情况下,我认为首先不需要扩展。你可以说:
var dict = [String: Set<String>]()
dict["key", default: []].insert("someValue")
使用 Dictionary
的下标重载采用默认值,如 SE-0165 中介绍的那样.
如果您仍然想要扩展,我建议您简单地使其更通用。例如,而不是约束 Value
至 Set
;将其约束到协议(protocol) SetAlgebra
(Set
符合)。
它表示可以执行类集合操作的类型,也派生自 ExpressibleByArrayLiteral
这意味着您可以使用上面的确切语法来实现您的方法:
extension Dictionary where Value : SetAlgebra {
mutating func insert(_ value: Value.Element, at key: Key) {
self[key, default: []].insert(value)
}
}
虽然这里要考虑的另一件事是 Swift 集合类型的写时复制行为,例如 Set
.在上述方法中,将查询字典中的给定键,返回该键的现有集合或新的空集合。你的value
然后将被插入到这个临时集合中,并且它将被重新插入到字典中。
这里使用临时表示如果集合已经在字典中,value
不会就地插入其中,将首先复制集合的缓冲区以保留值语义;这可能是一个性能问题(这在 this Q&A 和 this Q&A 中有更详细的探讨)。
尽管如此,我目前正在为 Dictionary
修复此问题的 subscript(_:default:)
in this pull request ,这样集合就可以发生变异。
虽然在修复之前,解决方案是在变异之前先从字典中删除集合:
extension Dictionary where Value : SetAlgebra {
mutating func insert(_ value: Value.Element, at key: Key) {
var set = removeValue(forKey: key) ?? []
set.insert(value)
self[key] = set
}
}
在这种情况下,使用扩展是完全合理的。
值得注意的是,这里使用协议(protocol)约束是解决没有参数化扩展问题的一般解决方案(或某些情况下的解决方法)。它允许您将所需的占位符实现为该协议(protocol)的关联类型。参见 this Q&A有关如何创建自己的协议(protocol)来实现该目的的示例。
关于swift - 如何将此效用函数转换为扩展函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45968332/
抛开可读性...在效率和/或功能方面,我不清楚将声明放在外部(我的做法)或循环内部(在其他 SO 帖子中看到)之间的区别。或者就此而言,为什么要代码声明?这里有一些退化的例子......下面有更多评论
我是一名优秀的程序员,十分优秀!