在灵丹妙药中我们有地图:
> 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种不同的句法变体),用例与地图重叠以及不明确的好处使语言复杂化。
使用关键字列表有什么好处?
答案 0 :(得分:139)
┌──────────────┬────────────┬───────────────────────┐
│ 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
作为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标准库,还有更多数据结构存储键/值对:
当您需要跨多个进程和/或VM存储键/值对时,您还有以下选项:
¹一般来说,但当然取决于™。
²最好的情况只是在列表前面。
³适用于Elixir 1.0.5及更高版本,旧版本可能会更慢。
⁴HashDict
现已弃用。
⁵需要线性搜索,平均扫描一半的元素。
答案 1 :(得分:8)
关键字列表的主要好处是与现有的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
答案 2 :(得分:2)
地图仅允许特定键的一个条目,而 关键字列表允许重复键。地图是有效的(特别是随着它们的增长),它们可以 用于Elixir的模式匹配。
通常,使用关键字列表来执行命令行参数和传递选项,并在需要关联数组时使用映射(或其他数据结构,HashDict)。