关键字列表有什么好处?
在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)。