Erlang地图之间的实际区别:删除/ 2和地图:没有/ 2

zxq*_*xq9 2 erlang

检查一个新的R17功能,地图的文档,带我到地图:删除/ 2和地图:没有/ 2.我能看到的唯一明显区别是remove/2只接受一个键并返回一个没有它的地图视图,其中没有/ 2接受一个列表并返回一个缺少列出的键的全新地图.

22> M1 = #{foo => bar, spam => eggs}.
#{foo => bar,spam => eggs}
23> M2 = maps:without([foo], M1).
#{spam => eggs}
24> M3 = maps:remove(foo, M1).
#{spam => eggs}
25> M1.
#{foo => bar,spam => eggs}
26> M2.
#{spam => eggs}
27> M3.
#{spam => eggs}
Run Code Online (Sandbox Code Playgroud)

这有什么实际影响?我可以欣赏不想创建没有/ 2的巨型地图的内存副本,但为什么不删除/ 2接受列表?我假设存在一个以性能为导向的原因,为什么这两个函数以它们的方式存在,但是我很困惑何时我想在大多数情况下使用一个而不是另一个(意思是,我不认为维护巨大的地图通常是个好主意).

tko*_*wal 6

关于地图的第一件事是,实施可能会改变.正如FredHébert写的那样,向你学习一些Erlang地图章节:"OTP团队尊重旧口号:首先让它发挥作用,然后让它变得美丽,只有你需要,才能让它变得快速." 所以不要过分依赖这个答案.

目前该maps:without/2功能实现如下:

without(Ks, M) when is_list(Ks), is_map(M) ->
    maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]).
Run Code Online (Sandbox Code Playgroud)

如您所见,它遍历整个地图.它将其转换为列表,删除键并将其转换回映射.效率不高,但正如我所说:未来可能会发生变化.

maps:remove/2函数是一个NIF,这意味着它是用C语言编写的,并利用内部表示.说到这......在斯德哥尔摩Erlang工厂2013期间,Kenneth Lundin提到了地图内部代表(http://vimeo.com/69950294).实际上,有两个(sliedes来自谈话,我链接).

映射内部表示1

这个是用于少量的键.在此表示中,值集具有指向键集的指针,这意味着,如果更改值,而不是键 - 键将被共享.键也被排序.第二个,对于大量的键看起来像这样:

在此输入图像描述

所以它是一棵树,这意味着,如果你删除右子树上的一个键,你的新地图可以共享整个左子树.有关不可变数据结构的更清晰信息,您可以参考维基百科, ErlangVM应根据需要透明地在这些表示之间切换.

回答你的问题.如果要删除一个密钥 - 使用maps:remove/2,如果要删除多个密钥,请使用maps:without/2,因为创建新映射而不是操作旧映射可能更便宜.