在Scala中, immutable.Map
上的+ (k -> v)运算符返回一个新的 immutable.Map
,其中包含原始内容以及新的键/值对 . 同样,在C#中,ImmutableDictionary.add(k, v)返回一个新的,更新的 ImmutableDictionary
.
但是,在Swift中,Dictionary似乎只有变异的 updateValue(v, forKey: k)
函数和变异的 [k:v]
运算符 .
我想也许我可以用 flatten()
玩一些技巧,但没有运气:
let updated = [original, [newKey: newValue]].flatten()
grab 我
Cannot convert value of type '() -> FlattenCollection<[[String : AnyObject]]>'
to specified type '[String : AnyObject]'
如何从现有内容创建新的,修改过的不可变 Dictionary
?
Update: 根据this answer的说明,Swift词典是值类型,并且对此并不感兴趣 - 似乎必须有一个更清洁的开箱即用的替代方案 .
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
的编译器检查,而不是不同的实现类的问题意味着这是最好的可以做到的?
Update #2: 如Jules's answer中所述,修改不可变的字典会导致性能问题 . 对于我当前的用例( AttributedString
属性词典,它往往相当小),它仍然可以简化某些事情,值得做,但除非Swift实现共享状态不可变字典,否则它可能不是一个好主意 . case - 这是不将它作为内置功能的一个很好的理由 .
4 回答
最直接的做法是复制到变量,修改,然后重新分配回常量:
显然不是很漂亮,但它可以很容易地包装到一个函数中 .
我不相信除了滚动自己之外还有其他解决方案 .
是的,可变性是在存储上指定的,即变量,而不是值 .
现在没有内置的方法可以做到这一点 . 您可以使用扩展程序编写自己的程序(如下) .
但请记住,这很可能是字典,因为字典是写时复制的,而你正是这样做的(制作副本,然后改变它) . 你可以通过首先使用一个可变变量来避免这一切:-)
不幸的是,这是一个很好的问题,因为答案是"you can't" . 还没有,无论如何 - 其他人同意这应该加上,因为有一个Swift Evolution proposal for this (and some other missing Dictionary features) . 它目前是"awaiting review",所以你可能会看到一个
merged()
方法,它基本上是你未来Swift版本中的+
运算符!在此期间,您可以使用您的解决方案附加整个词典,或一次添加一个值:
不要尝试更新不可变字典,除非它是专为不变性而设计的 .
不可变字典通常使用数据结构(例如具有不可变节点的红/黑树,而不是可以在实例之间共享或类似),可以生成修改后的副本而无需复制整个内容,但只需要子集(即它们)有O(log(n))复制和修改操作)但大多数为可变系统设计然后与不可变接口一起使用的字典都没有,所以有O(n)复制和修改操作 . 当你的字典开始大于几百个节点时,你会真正注意到性能差异 .