簡體   English   中英

可變字典在 F# 中不可變

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

MapDictionary之間的區別在於實現,這意味着 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM