我有声明可变字典的代码,但是当我尝试更改元素时会出错。
编码:
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
.我知道对象本身是不可变的,但字典的内容应该是可变的。当我通过显式初始化字典来更改代码时,它变得可变:
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
是一个接口(interface),而不是一个类。这个接口(interface)可能有多种不同的实现。你甚至可以自己做一个。Dictionary
确实是这些实现之一。它支持界面的全部功能。
但这不是 dict
的实现。函数返回。让我们试试这个:
> let d = dict [(1,2)]
> d.GetType().FullName
"Microsoft.FSharp.Core.ExtraTopLevelOperators+DictImpl`3[...
结果是 dict
的实现函数返回是Microsoft.FSharp.Core.ExtraTopLevelOperators.DictImpl
- 一个名为 DictImpl
的类在 F# 标准库的内部深处定义。碰巧该接口(interface)上的某些方法会抛出
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
是实现,这意味着内存和运行时特性。Dictionary
是一个哈希表。它提供恒定时间的插入和检索,但要为此付出代价,它依赖于其 key 的一致散列,并且它的更新是破坏性的,这也带来了线程不安全。Map
被实现为一棵树。它提供对数插入和检索,但作为返回具有 persistent data structure 的好处.此外,它要求 key 具有可比性。试试这个:> type Foo() = class end
> let m = Map [(Foo(), "bar")]
error FS0001: The type 'Foo' does not support the 'comparison' constraint
比较键对于构建树至关重要。
关于dictionary - F# 中不可变的可变字典,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65059094/