Elixir:模式匹配对元组和映射的工作方式不同
Posted
技术标签:
【中文标题】Elixir:模式匹配对元组和映射的工作方式不同【英文标题】:Elixir: pattern matching works differently for tuples and maps 【发布时间】:2014-07-04 18:46:30 【问题描述】:在 Elixir 中,如果我尝试对以下两个元组进行模式匹配:
a = 1, 2
我收到匹配错误。但是如果我对两张地图做同样的事情:
%x: a = %x: 1, y: 2
它工作正常,a
绑定到 1。我可以看到为什么匹配两个元组会出错,但为什么匹配映射不会出错?
【问题讨论】:
不应高于:%x: a = x: 1, y: 2
是:%x: a = %x: 1, y: 2
?
@ericky 是的,应该,我编辑了。
【参考方案1】:
maps
,Elixir 中的主要键值存储,有一个有趣的功能,在模式匹配方面将它们与其他数据结构区分开来。
map
实际上可以仅对值的子集进行模式匹配。模式中的键必须存在于匹配中,但两个结构不必像 list
或 tuple
那样相互镜像。例如:
iex(1)> [a, b] = [1, 2, 3]
** (MatchError) no match of right hand side value: [1, 2, 3]
iex(1)> a, b = 1, 2, 3
** (MatchError) no match of right hand side value: 1, 2, 3
iex(1)> %:a => one = %:a => 1, :b => 2, :c =>3
%a: 1, b: 2, c: 3
iex(2)> one
1
iex(3)> %:a => one, :c => three = %:a => 1, :b => 2, :c =>3
%a: 1, b: 2, c: 3
iex(4)> three
3
iex(5)> one
1
iex(6)> % = %:a => 1, :b => 2, :c =>3
%a: 1, b: 2, c: 3
iex(7)> %:d => four = %:a => 1, :b => 2, :c =>3
** (MatchError) no match of right hand side value: %a: 1, b: 2, c: 3
iex(8)> %:a => one, :d => four = %:a => 1, :b => 2, :c =>3
** (MatchError) no match of right hand side value: %a: 1, b: 2, c: 3
如果模式的数据结构与匹配的数据结构(即大小)不同,我们可以看到list
和tuple
都不匹配。而地图并非如此。因为模式和匹配中都存在密钥,所以匹配成功,无论模式和匹配的大小不同。空的map
也会匹配。
但是,如果模式中的键不在匹配中,则匹配不会成功。即使有匹配的键也是如此。因此,模式中使用的任何键都必须存在于匹配中。
您将在 Phoenix 框架中广泛看到这种匹配,以仅从请求中获取必要的参数。
来源:-Notes on Elixir: Pattern-Matching Maps
【讨论】:
【参考方案2】:在第一个示例中,您尝试将单个元素元组与两个元素元组进行匹配。在第二个示例中,您将匹配左右映射中的 :x 键。
编辑:我应该澄清 Elixir 中数据结构和模式匹配的规则。
在元组上进行匹配时,您需要提供一个匹配整个元组结构的模式。您可以使用_
,这是“catch-all”模式,但您仍然需要为元组的所有元素提供它。在列表上匹配时,您必须使用[a, b, c] = [1, 2, 3]
语法匹配所有元素,或者使用[h|t] = [1, 2, 3]
语法匹配头部和尾部。但是,在匹配映射时,您可以匹配映射中的一个或多个键,这为您提供了%a: b = %a: :foo, b: :bar
语法。
数据结构之间的语义有点不同,但都是相当常识的。元组规则的存在是因为两个元组不能相同,除非它们具有相同数量的元素,列表具有相同的限制,但是由于列表的语义,访问列表的头元素是使用时最常见的操作他们,因此[h|t]
语法。但是 Map 可以根据特定的键进行匹配,因此元素的数量无关紧要,只要匹配的双方包含相同的键,并且值的可选模式,那么它就是成功的匹配。
【讨论】:
我明白了。需要明确的是,地图是唯一允许部分模式匹配的数据结构? 是的,没错。其他一切都需要模式来匹配整个结构。以上是关于Elixir:模式匹配对元组和映射的工作方式不同的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Python 中对元组列表列表进行平面映射? [复制]