关键字列表有啥好处?

Posted

技术标签:

【中文标题】关键字列表有啥好处?【英文标题】: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 种不同的语法变体)、用例与映射重叠以及不明确的好处使语言复杂化。

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

【问题讨论】:

【参考方案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)。

【讨论】:

以上是关于关键字列表有啥好处?的主要内容,如果未能解决你的问题,请参考以下文章

将 ref 关键字与引用类型参数一起使用有啥好处?

在 C# 中将字段标记为“只读”有啥好处?

Solr:长度归一化/omitNorms=false 有啥好处?

java中final和static修饰变量有啥好处

java中啥时候该用static修饰方法?有啥好处或者坏处?

Java的多接口实现到底有啥好处?