【问题标题】:Practical difference between Erlang maps:remove/2 and maps:without/2Erlang maps:remove/2 和 maps:without/2 的实际区别
【发布时间】:2014-09-11 05:35:00
【问题描述】:

查看documentation for one of the new R17 features,maps,将我带到 maps:remove/2 和 maps:without/2。我能看到的唯一明显区别是 remove/2 采用单个键并返回没有它的地图视图,其中 without/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}

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

【问题讨论】:

    标签: erlang


    【解决方案1】:

    关于地图的第一件事是,实现可能会发生变化。正如 Fred Hébert 在learn you some Erlang maps Chapter 中所写:“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)]).
    

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

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

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

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

    回答你的问题。如果你想删除一个键 - 使用maps:remove/2,如果你想删除多个键,使用maps:without/2,因为创建新地图而不是操作旧地图可能更便宜。

    【讨论】:

    • 很好的解释。这让我很多更深入地了解这些是如何工作的(而不仅仅是删除/2 和没有/2)。我现在必须看那个视频。
    • 我们还没有到第二个表示。
    猜你喜欢
    • 1970-01-01
    • 2017-06-14
    • 2016-02-10
    • 1970-01-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-01
    相关资源
    最近更新 更多