【问题标题】:What is the benefit of Keyword Lists?关键字列表有什么好处?
【发布时间】:2015-03-26 15:37:54
【问题描述】:

在长生不老药中,我们有地图:

> map = %{:a => "one", :b => "two"} # = %{a: "one", b: "two"}
> map.a                             # = "one"
> map[:a]                           # = "one"

我们也有关键字列表:

> kl = [a: "one", b: "two"]       # = [a: "one", b: "two"]
> kl2 = [{:a, "one"},{:b, "two"}] # = [a: "one", b: "two"]
> kl == kl2                       # = true
> kl[:a]                          # = "one"
> kl.a                            # = ** (ArgumentError)

为什么两者兼而有之?

语法? 是不是因为关键字列表具有更灵活的语法,允许将它们定义为没有大括号甚至没有括号作为函数调用的最后一个参数?那为什么不给 Maps 这个语法糖呢?

重复键?是因为关键字列表可以有重复键吗?为什么要同时使用 Map 样式访问和重复键?

性能?是因为关键字列表的性能更好吗?那为什么有地图?在通过键查找成员方面,map 不应该比元组列表更高效吗?

JS Array 和 Ruby Hash 的样子?是这样吗?

我了解它们在结构上是不同的数据表示形式。在我看来,elixir 中的关键字列表似乎通过特殊的语法(3 种不同的语法变体)、用例与映射重叠以及不明确的好处使语言复杂化。

使用关键字列表有什么好处?

【问题讨论】:

    标签: elixir


    【解决方案1】:
    Keyword List Map/Struct HashDict (deprecated)
    Duplicate keys yes no no
    Ordered yes no no
    Pattern matching yes  yes no
    Performance¹ (Insert) very fast² fast³ fast⁴
    Performance¹ (Access) slow⁵ fast³ fast⁴

    关键字列表是轻量级的,并且在它们下面有一个简单的结构,这使得它们非常灵活。您可以将它们视为 Erlang 约定之上的语法糖,从而可以轻松地与 Erlang 交互,而无需编写太丑陋的代码。例如,关键字列表用于表示函数参数,这是从 Erlang 继承的属性。在某些情况下,关键字列表是您唯一的选择,尤其是在您需要重复键或排序时。它们只是具有与其他替代品不同的属性,这使得它们更适合某些情况而不太适合其他情况。

    映射(和结构)用于存储实际的有效负载数据,因为它们具有基于哈希的实现。关键字列表在内部只是每个操作都需要遍历的列表,因此它们不具备常量时间访问等经典键值数据结构的属性。

    Elixir 还引入了HashDict 作为poor performance of maps at the time it was written 的解决方法。但是,从 Elixir 1.0.5/Erlang 18.0 和 HashDict will be deprecated in future versions 开始,此问题现已得到修复。

    如果深入研究 Erlang 标准库,就会发现存储键/值对的数据结构更多:

    • proplists – 类似于 Elixir 关键字列表
    • maps – 与 Elixir 地图相同
    • dict – 基于 Erlang 原语构建的键值字典
    • gb_trees – 一般平衡树

    当您需要跨多个进程和/或虚拟机存储键/值对时,您还可以使用以下选项:

    • ets/dets –(基于磁盘)Erlang 术语存储
    • mnesia – 分布式数据库

    ¹ 一般而言,但当然 这取决于™。

    ² 最好的情况只是添加到列表之前。

    ³ 适用于 Elixir 1.0.5 及更高版本,在旧版本中可能会较慢。

    HashDict 现已弃用。

    ⁵ 需要平均扫描一半元素的线性搜索。

    【讨论】:

    • 允许重复键和被排序不是好处,而是不同的属性。您需要选择适合您需求的数据结构。
    • 严格来说,是的,但如果你需要这些属性,它们可能会带来好处——这就是我的意思。
    • @PatrickOscity:在这种情况下,它们肯定会更好地归类为需求吗?
    • @greggreg 拥有关键字列表还有一个隐含的好处:我们区分了结构化和非结构化数据。对于具有一组已知键的结构化数据,地图非常有用,而关键字则不然。今天,大多数地图的使用都是针对结构化数据的,我们将关键字留给可选的。如果我们只有地图,我认为这种区别的很大一部分将会丢失。
    • 事实上确实如此,地图是从 erlang 18 开始的方式。
    【解决方案2】:

    关键字列表的主要好处是向后兼容现有的 elixir 和 erlang 代码库。

    如果用作类似于例如的函数参数,它们还会添加语法糖。 ruby 语法:

    def some_fun(arg, opts \\ []), do: ...
    some_fun arg, opt1: 1, opt2: 2
    

    使用关键字列表的主要缺点是无法对它们执行部分模式匹配:

    iex(1)> m = %{a: 1, b: 2}
    %{a: 1, b: 2}
    iex(2)> %{a: a} = m
    %{a: 1, b: 2}
    iex(3)> a
    1
    iex(4)> k = [a: 1, b: 2]
    [a: 1, b: 2]
    iex(5)> [a: a] = k
    ** (MatchError) no match of right hand side value: [a: 1, b: 2]
    

    让我们将它扩展到函数参数。想象一下,我们需要根据其中一个选项的值来处理一个多子句函数:

    def fun1(arg, opt1: opt1) when is_nil(opt1), do: do_special_thing
    def fun1(arg, opts), do: do_regular_thing
    
    def fun2(arg, %{opt1: opt1}) when is_nil(opt1), do: do_special_thing
    def fun2(arg, opts), do: do_regular_thing
    

    这永远不会执行do_special_thing:

    fun1("arg", opt1: nil, opt2: "some value")
    doing regular thing  
    

    使用地图参数它会起作用:

    fun2("arg", %{opt1: nil, opt2: "some value"})
    doing special thing
    

    【讨论】:

      【解决方案3】:

      地图只允许一个特定键的条目,而 关键字列表允许重复关键字。地图是有效的(特别是随着它们的增长),它们可以 在 Elixir 的模式匹配中使用。

      一般来说,对命令行参数和传递选项等内容使用关键字列表,并在需要关联数组时使用映射(或另一种数据结构,HashDict)。

      【讨论】:

        猜你喜欢
        • 2011-03-16
        • 2012-04-28
        • 2010-10-07
        • 2014-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多