[英]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.