Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Dictionary 批量添加到地图,单位为F#_Dictionary_F# - Fatal编程技术网

Dictionary 批量添加到地图,单位为F#

Dictionary 批量添加到地图,单位为F#,dictionary,f#,Dictionary,F#,我有一个简单的类型: type Token = { Symbol: string Address: string Decimals: int } 和内存缓存(它们位于数据库中): 让可变私有令牌缓存:Map=Map.empty 令牌模块的一部分 有时,我会以令牌数组的形式添加一些新条目,并希望更新缓存 这种情况很少发生(每百万次读取不到一次) 当我用新批更新数据库时,我也想更新缓存映射,我刚刚写了以下内容: t

我有一个简单的类型:

type Token =
    {
        Symbol:     string
        Address:    string
        Decimals:   int
    }
和内存缓存(它们位于数据库中):

让可变私有令牌缓存:Map=Map.empty
令牌模块的一部分

有时,我会以令牌数组的形式添加一些新条目,并希望更新缓存

这种情况很少发生(每百万次读取不到一次)

当我用新批更新数据库时,我也想更新缓存映射,我刚刚写了以下内容:

tokenCache <- tokens |> Seq.fold (fun m i -> m.Add(i.Symbol, i)) tokenCache
tokenCache Seq.fold(fun m i->m.Add(i.Symbol,i))tokenCache
由于这种情况很少发生,我并不真正关心性能,因此这个问题出于好奇:


当我这样做时,映射将在令牌数组中的每个条目中重新创建一次:10个新令牌,10个映射重新创建。我认为这是处理这件事的最“F”的方式。这让我思考:将映射转换为KVP列表、获取distinct的输出并重新创建映射不是更有效吗?或者还有其他方法我没有考虑过吗?

这不是对上述问题的回答,而是对您在评论中提出的问题的澄清。

您所表达的前提是不正确的:

该映射将在令牌数组中的每个条目中重新创建一次

实际上,并不是每次插入都会完全重新创建贴图。但与此同时,你在评论中表达的另一个假设也是不正确的:


所以不变性是从语言的角度来看的,编译器不会在幕后重新创建对象

不变性是真实的。但地图也不是每次都会被重新创建。有时是这样,但不是每次都是这样

我不打算确切地描述
Map
是如何工作的,因为这太复杂了。相反,我将在列表中说明该原理


F#列表是“单链表”,这意味着每个列表由两部分组成:(1)第一个元素(称为“head”)和(2)指向其余元素(称为“tail”)的引用(指针)。这里需要注意的关键是,“元素的其余部分”本身也是一个列表

因此,如果您声明这样的列表:

let x = [1; 2; 3]
x -> 1 -> 2 -> 3 -> []
y -> 42 -> 1 -> 2 -> 3 -> []
它将在内存中表示如下:

let x = [1; 2; 3]
x -> 1 -> 2 -> 3 -> []
y -> 42 -> 1 -> 2 -> 3 -> []
名称
x
是对第一个元素的引用,然后每个元素都有对下一个元素的引用,最后一个元素是对空列表的引用。到目前为止还不错

现在,让我们看看如果将新元素添加到此列表中会发生什么:

let y = 42 :: x
现在列表
y
将如下所示:

let x = [1; 2; 3]
x -> 1 -> 2 -> 3 -> []
y -> 42 -> 1 -> 2 -> 3 -> []
但是这张照片缺了一半。如果我们将内存放在比
y
更广的范围内,我们会看到:

    x -> 1 -> 2 -> 3 -> []
         ^
         |
        /
y ->  42
因此,您可以看到
y
列表由两部分组成(与所有列表一样):第一个元素
42
和对其余元素
1->2->3
的引用。但是“其余元素”位不是
y
所独有的,它有自己的名称
x

因此,你有两个列表,分别是3个和4个元素,但它们一起只占用4个内存单元,而不是7个

另外需要注意的是,当我创建
y
列表时,我不必从头开始重新创建整个列表,也不必将
1
2
3
x
复制到
y
。这些单元格保持在原来的位置,
y
只获得了对它们的引用

需要注意的第三点是,这意味着在列表中预先添加元素是一个O(1)操作。不复制涉及的列表

第四点(希望也是最后一点)需要注意的是,这种方法之所以可能是因为不变性。只有因为我知道
x
列表永远不会改变,我才能引用它。如果有变化,我只是以防万一


这种安排,其中数据结构的每次迭代都是在前一次迭代的基础上构建的,称为“”(更准确地说,它是一种持久数据结构)


它的工作方式对于链表来说很容易看到,但对于更复杂的数据结构也很有效,包括映射(以树的形式表示)。

映射并不是为每个元素都完全重新创建的。因此,从语言的角度来看,不可变性是,编译器不会在幕后重新创建对象?不,这也不正确。我将尝试发布一个答案。我理解这个想法(我来自asm/c背景),我假设列表是不可变的对象,例如指向节点的指针数组(如果添加到头部,则需要复制该数组,如果添加到末端,则可能需要扩展该数组)。所以我猜每个节点都有一个refcount和下一个字段,对吗?不,refcount是古老的,我认为除了STL之外,没有人再使用它们了。NET使用a来处理未引用的对象。好的,我明白了,所以它遍历对象图以查看哪些对象仍然处于活动状态;我记得几年前读过。很好,谢谢你的澄清。