[英]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
。
因此,您有兩個列表x
和y
,分別是 3 和 4 個元素,但它們一起只占用 memory 的 4 個單元格,而不是 7 個。
還有一點需要注意的是,當我創建y
列表時,我不必從頭開始重新創建整個列表,我不必將1
、 2
和3
從x
復制到y
。 這些單元格就在它們所在的位置,而y
只得到了對它們的引用。
第三點要注意的是,這意味着將元素添加到列表中是一個 O(1) 操作。 沒有復制所涉及的清單。
第四點(希望是最后的)要注意的是,這種方法只有在不變性的情況下才有可能。 只是因為我知道x
列表永遠不會改變,所以我可以參考它。 如果它可能發生變化,我將被迫復制它以防萬一。
這種安排,其中數據結構的每次迭代都構建在前一個“之上”,稱為“持久數據結構”(嗯,更准確地說,它是一種持久數據結構)。
它的工作方式對於鏈表很容易看出,但它也適用於更多涉及的數據結構,包括地圖(表示為樹)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.