簡體   English   中英

在 F# 中批量添加到 map

[英]bulk adding to a map, in F#

我有一個簡單的類型:

type Token =
    {
        Symbol:     string
        Address:    string
        Decimals:   int
    }

和一個 memory 緩存(它們在數據庫中):

let mutable private tokenCache : Map<string, Token> = Map.empty

令牌模塊的一部分。

有時我會以Token array的形式添加一些新條目,並且我想更新緩存。

它很少發生(每百萬次讀取不到一次)。

當我用新批次更新數據庫時,我也想更新緩存 map,我剛剛寫了這個:

tokenCache <- tokens |> Seq.fold (fun m i -> m.Add(i.Symbol, i)) tokenCache

由於這種情況很少發生,我並不真正關心性能,所以這個問題是出於好奇:

當我這樣做時,map 將在令牌數組中的每個條目重新創建一次:10 個新令牌,10 個 map 重新創建。 我認為這是處理這個問題的最“F#”方式。 這讓我想到:將 map 轉換為 KVP 列表,獲得不同的 output 並重新創建 map 不是更有效嗎? 還是有另一種我沒有想到的方法?

這不是對所述問題的回答,而是對您在評論中提出的問題的澄清。

你表達的這個前提是不正確的:

map 將在令牌數組中的每個條目重新創建一次

map 實際上並沒有為每次插入完全重新創建。 但同時,您在評論中表達的另一個假設也是不正確的:

所以不變性是從語言的角度來看的,編譯器不會在幕后重新創建 object?

不變性是真實的。 但是 map 也不會每次都重新創建。 有時確實如此,但並非每次都如此。

我不會准確描述Map的工作原理,因為這太復雜了。 相反,我將在列表中說明原理。


F# 列表是“單鏈表”,這意味着每個列表包含兩件事:(1)第一個元素(稱為“頭”)和(2)對 rest 元素(稱為“尾”)的引用(指針)。 這里要注意的關鍵是“其余元素”部分本身也是一個列表。

因此,如果您聲明這樣的列表:

let x = [1; 2; 3]

它將在 memory 中表示,如下所示:

x -> 1 -> 2 -> 3 -> []

名稱x是對第一個元素的引用,然后每個元素都有對下一個元素的引用,最后一個 - 對空列表的引用。 到目前為止,一切都很好。

現在讓我們看看如果向這個列表中添加一個新元素會發生什么:

let y = 42 :: x

現在列表y將表示如下:

y -> 42 -> 1 -> 2 -> 3 -> []

但這幅畫少了一半。 如果我們在比y更寬的 scope 中查看 memory ,我們會看到:

    x -> 1 -> 2 -> 3 -> []
         ^
         |
        /
y ->  42

因此,您會看到y列表包含兩件事(就像所有列表一樣):第一個元素42和對元素1->2->3的 rest 的引用。 但是“其余元素”位不是y獨有的,它有自己的名稱x

因此,您有兩個列表xy ,分別是 3 和 4 個元素,但它們一起只占用 memory 的 4 個單元格,而不是 7 個。

還有一點需要注意的是,當我創建y列表時,我不必從頭開始重新創建整個列表,我不必將123x復制到y 這些單元格就在它們所在的位置,而y只得到了對它們的引用。

第三點要注意的是,這意味着將元素添加到列表中是一個 O(1) 操作。 沒有復制所涉及的清單。

第四點(希望是最后的)要注意的是,這種方法只有在不變性的情況下才有可能。 只是因為我知道x列表永遠不會改變,所以我可以參考它。 如果它可能發生變化,我將被迫復制它以防萬一。


這種安排,其中數據結構的每次迭代都構建在前一個“之上”,稱為“持久數據結構”(嗯,更准確地說,它是一種持久數據結構)。

它的工作方式對於鏈表很容易看出,但它也適用於更多涉及的數據結構,包括地圖(表示為樹)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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