关键字列表有什么好处?

在elixir我们有地图:

> 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) 

为什么呢?

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

重复键? 是否因为关键字列表可以有重复的键? 你为什么要同时使用Map风格访问和重复键?

性能? 是否因为关键字列表有更好的performance? 那么为什么有地图? 而不应该更多的表演查询成员的关键比元组列表?

JS数组和Ruby哈希像外观? 是吗?

我理解他们在结构上是不同的数据表示。 对我来说,elixir中的关键字列表似乎通过特殊的语法(3种不同的语法变体),用例与地图重叠,以及不明确的好处而使语言复杂化。

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

  ┌──────────────┬────────────┬───────────────────────┐ │ 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⁴ │ │ └ Access │ slow⁵ │ fast³ │ fast⁴ │ └──────────────────┴──────────────┴────────────┴───────────────────────┘ 

关键字列表是轻量级的,在它们下面有一个简单的结构,这使得它们非常灵活。 你可以把它们看作是Erlang约定的语法糖,这样就可以很容易地与Erlang进行交互,而不用编写太难看的代码。 例如,关键字列表用于表示函数参数,这是从Erlanginheritance的属性。 在某些情况下,关键字列表是您唯一的select,特别是如果您需要重复键或sorting。 他们只是具有不同于其他select的属性,这使得它们更适合某些情况,更less适用于其他情况。

Maps(和Structs)用于存储实际的有效载荷数据,因为它们具有基于散列的实现。 内部关键字列表只是每个操作需要遍历的列表,所以它们不具有经典键值数据结构(如恒定时间访问)的属性。

Elixir还介绍了HashDict作为解决地图性能差的方法。 但是,现在已经修复了Elixir 1.0.5 / Erlang 18.0,并且在将来的版本中将不推荐使用 HashDict

如果你深入挖掘Erlang标准库,还有更多的数据结构存储键/值对:

  • proplists – 类似Elixir关键字列表
  • 地图 – 与Elixir地图相同
  • dict – 从Erlang基元构build的键值字典
  • gb_trees – 一般平衡树

当您需要在多个进程和/或VM中存储键/值对时,您还可以使用这些选项:

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

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

²最好的情况只是列在一个列表上。

³适用于Elixir 1.0.5及以上版本,在旧版本中可能会变慢。

HashDict现在被弃用了。

⑤需要线性search,平均扫描一半的元素。

关键字列表的主要优点是与现有的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 

地图只允许一个特定的键入口,而关键字列表允许键重复。 地图是高效的(尤其是在他们成长的时候),他们可以在Elixir的模式匹配中使用。

一般情况下,使用关键字列表来处理命令行参数和传递选项,并且在需要关联数组时使用映射(或其他数据结构,HashDict)。