[英]mutable dictionary immutable in F#
我有声明可变字典的代码,但是当我尝试更改元素时会出错。
编码:
let layers =
seq {
if recipes.ContainsKey(PositionSide.Short) then yield! buildLayerSide recipes.[PositionSide.Short]
if recipes.ContainsKey(PositionSide.Long) then yield! buildLayerSide recipes.[PositionSide.Long]
}
|> Seq.map (fun l -> l.Id, l)
|> dict
这将创建一个IDictionary
。 我知道 object 本身是不可变的,但字典的内容应该是可变的。
当我通过显式初始化字典来更改代码时,它变得可变:
let layers =
let a =
seq {
if recipes.ContainsKey(PositionSide.Short) then yield! buildLayerSide recipes.[PositionSide.Short]
if recipes.ContainsKey(PositionSide.Long) then yield! buildLayerSide recipes.[PositionSide.Long]
}
|> Seq.map (fun l -> l.Id, l)
|> dict
let x = Dictionary<string, Layer>()
a
|> Seq.iter (fun kvp -> x.[kvp.Key] <- kvp.Value)
x
这是为什么?
IDictionary
是一个接口,而不是 class。 这个接口可能有多种不同的实现。 你甚至可以自己做一个。
Dictionary
确实是这些实现之一。 它支持界面的全部功能。
但这不是dict
function 返回的实现。 让我们试试这个:
> let d = dict [(1,2)]
> d.GetType().FullName
"Microsoft.FSharp.Core.ExtraTopLevelOperators+DictImpl`3[...
结果发现dict
function 返回的实现是Microsoft.FSharp.Core.ExtraTopLevelOperators.DictImpl
- 一个名为DictImpl
的 class 在 Z629B160731F353947EAC 标准库的内部深处定义
碰巧该接口上的某些方法会抛出NotSupportedException
:
> d.Add(4,5)
System.NotSupportedException: This value cannot be mutated
这是设计使然。 它是故意这样做的,以支持“默认情况下的不变性”。
如果你真的想要一个可变版本,你可以使用Dictionary
的构造函数之一创建一个副本:
> let m = Dictionary(d)
> m.Add(4,5) // Works now
Map
和Dictionary
之间的区别在于实现,这意味着 memory 和运行时特性。
Dictionary
是一个哈希表。 它提供恒定时间的插入和检索,但要为此付出代价,它依赖于其密钥的一致散列,并且它的更新是破坏性的,这也带来了线程不安全。
Map
实现为树。 它提供对数插入和检索,但作为回报具有持久数据结构的好处。 此外,它要求密钥具有可比性。 尝试这个:
> type Foo() = class end
> let m = Map [(Foo(), "bar")]
error FS0001: The type 'Foo' does not support the 'comparison' constraint
比较键对于构建树至关重要。
不同之处在于dict
是一个只读字典,其中包含一些引发异常的变异方法(这是该类型的一个缺点),而 map 是一个不可变集合,它使用Map
模块中的函数以及修改元素的方法map 并返回一份副本。 Get Programming with F#一书的第 17 课对此有很好的解释。
此外,对于dict
,“使用其键检索值非常快,接近 O(1),因为 Dictionary class 是作为 hash 表实现的。” 来自文档; 而map
基于二叉树,因此使用其键检索值具有 O(log(N)) 复杂度。 请参阅集合类型。 这也意味着map
中的键是有序的; 而在dict
中,它们是无序的。
对于许多用例,性能差异可以忽略不计,因此函数式编程风格的默认选择应该是map
,因为它的编程接口在风格上与其他list
seq
类似。
dict
是创建iDictionary
的辅助方法,该字典是不可变的(因此您需要在创建对象期间提供内容)。 它实际上是一个只读字典,所以你不能修改它的内容也就不足为奇了。 在您的第二个示例中,您显式创建了一个Dictionary
,它是一个可变字典。 由于 Dictionary 可以使用 iDictionary,因此您只需将 iDictionary 传递给它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.