在灵丹妙药中我们有地图:
> 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)
两个为什么?
句法?是因为关键字列表具有更灵活的语法,允许它们被定义为没有卷曲,甚至没有括号作为函数调用的最后一个参数?那为什么不给这个语法糖吗?
重复密钥?是因为关键字列表可以有重复的键吗?为什么要同时使用Map样式访问和重复键?
性能?是因为关键字列表有更好的表现吗?那为什么要有地图?并且不应该通过键查找成员比使用元组列表更高效吗?
JS Array和Ruby Hash一样外观?是吗?
我理解结构上它们是不同的数据表示.对我而言,似乎elixir中的关键字列表通过特殊语法(3种不同的句法变体),用例与地图重叠以及不明确的好处使语言复杂化.
使用关键字列表有什么好处?
1> Patrick Osci..:
?????????????????????????????????????????????????????
? 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进行交互.例如,关键字列表用于表示函数参数,这是从Erlang继承的属性.在某些情况下,关键字列表是您唯一的选择,特别是如果您需要重复键或订购.它们具有与其他替代品不同的特性,这使得它们更适合某些情况而不适合其他情况.
Maps(和Structs)用于存储实际的有效负载数据,因为它们具有基于散列的实现.内部的关键字列表只是每个操作需要遍历的列表,因此它们不具有经典键值数据结构的属性,如常量时间访问.
Elixir还介绍HashDict
了在编写地图时地图表现不佳的解决方法.但是,现在从Elixir 1.0.5/Erlang 18.0开始修复HashDict
,将来的版本将不推荐使用.
如果深入研究Erlang标准库,还有更多存储键/值对的数据结构:
proplists - 类似于Elixir关键字列表
地图 - 与Elixir地图相同
dict - 从Erlang原语构建的键值字典
gb_trees - 一般平衡树
当您需要跨多个进程和/或VM存储键/值对时,您还可以使用以下选项:
ets/dets - (基于磁盘)Erlang术语存储
mnesia - 分布式数据库
¹一般来说,但当然取决于 ™.
²最好的情况只是在列表前面.
³适用于Elixir 1.0.5及更高版本,在旧版本中可能会更慢.
⁴ HashDict
现在被弃用了.
⁵需要线性搜索,平均扫描一半的元素.
@greggreg使用关键字列表还有另一个隐含的好处:我们对结构化和非结构化数据进行区分.地图对于具有已知密钥集的结构化数据非常有用,而关键字则不是.今天,大多数地图用于结构化数据,我们为可选数据留下关键字.如果我们只有地图,我认为这种区别的很大一部分将会丢失.
严格来说,是的,但如果您需要这些属性,它们可能会带来好处 - 这就是我的意思.
2> Voldy..:
关键字列表的主要好处是与现有的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
使用map参数,它将起作用:
fun2("arg", %{opt1: nil, opt2: "some value"})
doing special thing