繁体   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