我如何 "append" 到 Swift 中的不可变字典?
How do I "append" to an immutable dictionary in Swift?
在 Scala 中,+ (k -> v)
operator on immutable.Map
returns a new immutable.Map
with the contents of the original, plus the new key/value pair. Similarly, in C#, ImmutableDictionary.add(k, v)
returns 一个新的、更新的 ImmutableDictionary
。
然而,在 Swift 中,Dictionary
似乎只有变异 updateValue(v, forKey: k)
函数和变异 [k:v]
运算符。
我想也许我可以用 flatten()
玩点小把戏,但运气不好:
let updated = [original, [newKey: newValue]].flatten()
让我明白
Cannot convert value of type '() -> FlattenCollection<[[String : AnyObject]]>'
to specified type '[String : AnyObject]'
如何从现有的内容创建新的修改后的不可变 Dictionary
?
更新: 基于 this answer's note that Swift dictionaries are value types, and this answer 的可变版本,我想出了以下扩展运算符,但我对此并不兴奋——看起来就像必须有一个更干净的开箱即用的替代品。
func + <K, V>(left: [K:V], right: [K:V]) -> [K:V] {
var union = left
for (k, v) in right {
union[k] = v
}
return union
}
但也许事实(如果我理解正确的话)Swift 字典的不变性是对 let
的编译器检查而不是不同实现的问题 类 意味着这是能做到最好吗?
更新 #2: 如 所述,修改未专门优化以在副本之间共享状态的不可变字典(如 Swift 字典aren't) 会出现性能问题。对于我当前的用例(AttributedString
属性字典,它往往相当小)它可能仍然简化了某些值得做的事情,但是直到并且除非 Swift 实现共享状态不可变字典它是在一般情况下可能不是一个好主意——这是不将其作为内置功能的一个很好的理由。
目前没有执行此操作的内置方法。您可以使用扩展名(如下)编写自己的代码。
但请记住,这可能会 复制 字典,因为字典是写时复制的,而您正是这样做的(制作副本,然后变异它)。你可以通过首先使用一个可变变量来避免这一切:-)
extension Dictionary {
func updatingValue(_ value: Value, forKey key: Key) -> [Key: Value] {
var result = self
result[key] = value
return result
}
}
let d1 = ["a": 1, "b": 2]
d1 // prints ["b": 2, "a": 1]
let d2 = d1.updatingValue(3, forKey: "c")
d1 // still prints ["b": 2, "a": 1]
d2 // prints ["b": 2, "a": 1, "c": 3]
最直接的做法是复制到一个变量,修改,然后重新赋值回一个常量:
var updatable = original
updatable[newKey] = newValue
let updated = updatable
显然不漂亮,但可以很容易地将其包装到一个函数中。
extension Dictionary {
func addingValue(_ value: Value, forKey key: Key) -> Dictionary<Key, Value> {
// Could add a guard here to enforce add not update, if needed
var updatable = self
updatable[key] = value
return updatable
}
}
let original = [1 : "One"]
let updated = original.addingValue("Two", forKey: 2)
除了自己动手,我认为没有其他解决方案。
But maybe the fact (if I understand correctly) that the immutability of Swift dictionaries is a compiler check on let
对,可变性是在storage上指定的,即变量,而不是在value.
上
不幸的是,这是一个很好的问题,因为答案是 "you can't"。无论如何,还没有——其他人同意应该添加这个,因为有一个 Swift Evolution proposal for this (and some other missing Dictionary
features)。它目前是 "awaiting review",因此您可能会在 Swift!
的未来版本中看到一个 merged()
方法,它基本上是您的 +
运算符
同时,您可以使用您的解决方案追加整个字典,或一次追加一个值:
extension Dictionary {
func appending(_ key: Key, _ value: Value) -> [Key: Value] {
var result = self
result[key] = value
return result
}
}
不要尝试更新不可变字典,除非它是专门为不可变而设计的。
不可变字典通常使用一种数据结构(例如 red/black 具有不可变节点的树,这些节点可以在实例之间共享或类似)可以生成修改后的副本,而无需复制整个内容,但只有一个子集(即它们具有 O(log(n)) 复制和修改操作),但大多数为可变系统设计然后与不可变接口一起使用的词典却没有,因此具有 O(n) 复制-和修改操作。当您的字典开始变得超过几百个节点时,您会真正注意到性能差异。
在 Scala 中,+ (k -> v)
operator on immutable.Map
returns a new immutable.Map
with the contents of the original, plus the new key/value pair. Similarly, in C#, ImmutableDictionary.add(k, v)
returns 一个新的、更新的 ImmutableDictionary
。
在 Swift 中,Dictionary
似乎只有变异 updateValue(v, forKey: k)
函数和变异 [k:v]
运算符。
我想也许我可以用 flatten()
玩点小把戏,但运气不好:
let updated = [original, [newKey: newValue]].flatten()
让我明白
Cannot convert value of type '() -> FlattenCollection<[[String : AnyObject]]>'
to specified type '[String : AnyObject]'
如何从现有的内容创建新的修改后的不可变 Dictionary
?
更新: 基于 this answer's note that Swift dictionaries are value types, and this answer 的可变版本,我想出了以下扩展运算符,但我对此并不兴奋——看起来就像必须有一个更干净的开箱即用的替代品。
func + <K, V>(left: [K:V], right: [K:V]) -> [K:V] {
var union = left
for (k, v) in right {
union[k] = v
}
return union
}
但也许事实(如果我理解正确的话)Swift 字典的不变性是对 let
的编译器检查而不是不同实现的问题 类 意味着这是能做到最好吗?
更新 #2: 如 AttributedString
属性字典,它往往相当小)它可能仍然简化了某些值得做的事情,但是直到并且除非 Swift 实现共享状态不可变字典它是在一般情况下可能不是一个好主意——这是不将其作为内置功能的一个很好的理由。
目前没有执行此操作的内置方法。您可以使用扩展名(如下)编写自己的代码。
但请记住,这可能会 复制 字典,因为字典是写时复制的,而您正是这样做的(制作副本,然后变异它)。你可以通过首先使用一个可变变量来避免这一切:-)
extension Dictionary {
func updatingValue(_ value: Value, forKey key: Key) -> [Key: Value] {
var result = self
result[key] = value
return result
}
}
let d1 = ["a": 1, "b": 2]
d1 // prints ["b": 2, "a": 1]
let d2 = d1.updatingValue(3, forKey: "c")
d1 // still prints ["b": 2, "a": 1]
d2 // prints ["b": 2, "a": 1, "c": 3]
最直接的做法是复制到一个变量,修改,然后重新赋值回一个常量:
var updatable = original
updatable[newKey] = newValue
let updated = updatable
显然不漂亮,但可以很容易地将其包装到一个函数中。
extension Dictionary {
func addingValue(_ value: Value, forKey key: Key) -> Dictionary<Key, Value> {
// Could add a guard here to enforce add not update, if needed
var updatable = self
updatable[key] = value
return updatable
}
}
let original = [1 : "One"]
let updated = original.addingValue("Two", forKey: 2)
除了自己动手,我认为没有其他解决方案。
But maybe the fact (if I understand correctly) that the immutability of Swift dictionaries is a compiler check on
let
对,可变性是在storage上指定的,即变量,而不是在value.
上不幸的是,这是一个很好的问题,因为答案是 "you can't"。无论如何,还没有——其他人同意应该添加这个,因为有一个 Swift Evolution proposal for this (and some other missing Dictionary
features)。它目前是 "awaiting review",因此您可能会在 Swift!
merged()
方法,它基本上是您的 +
运算符
同时,您可以使用您的解决方案追加整个字典,或一次追加一个值:
extension Dictionary {
func appending(_ key: Key, _ value: Value) -> [Key: Value] {
var result = self
result[key] = value
return result
}
}
不要尝试更新不可变字典,除非它是专门为不可变而设计的。
不可变字典通常使用一种数据结构(例如 red/black 具有不可变节点的树,这些节点可以在实例之间共享或类似)可以生成修改后的副本,而无需复制整个内容,但只有一个子集(即它们具有 O(log(n)) 复制和修改操作),但大多数为可变系统设计然后与不可变接口一起使用的词典却没有,因此具有 O(n) 复制-和修改操作。当您的字典开始变得超过几百个节点时,您会真正注意到性能差异。