查看源代码 模式匹配
在本章中,我们将学习为什么 Elixir 中的 =
运算符被称为匹配运算符,以及如何在数据结构中使用它进行模式匹配。我们将学习如何使用固定运算符 ^
来访问先前绑定的值。
匹配运算符
我们已经使用了几次 =
运算符来在 Elixir 中分配变量
iex> x = 1
1
iex> x
1
在 Elixir 中,=
运算符实际上被称为 *匹配运算符*。让我们看看为什么
iex> x = 1
1
iex> 1 = x
1
iex> 2 = x
** (MatchError) no match of right hand side value: 1
请注意,1 = x
是一个有效的表达式,它匹配是因为左右两边都等于 1。当两边不匹配时,将引发 MatchError
错误。
变量只能分配在 =
的左侧
iex> 1 = unknown
** (CompileError) iex:1: undefined variable "unknown"
模式匹配
匹配运算符不仅用于匹配简单值,而且对于解构更复杂的数据类型也很有用。例如,我们可以对元组进行模式匹配
iex> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}
iex> a
:hello
iex> b
"world"
如果两边无法匹配,例如元组的大小不同,则会发生模式匹配错误
iex> {a, b, c} = {:hello, "world"}
** (MatchError) no match of right hand side value: {:hello, "world"}
在比较不同类型时也是如此,例如将左侧的元组与右侧的列表进行匹配
iex> {a, b, c} = [:hello, "world", 42]
** (MatchError) no match of right hand side value: [:hello, "world", 42]
更有趣的是,我们可以匹配特定值。以下示例断言,只有当右侧是元组且以原子 :ok
开头时,左侧才会匹配右侧
iex> {:ok, result} = {:ok, 13}
{:ok, 13}
iex> result
13
iex> {:ok, result} = {:error, :oops}
** (MatchError) no match of right hand side value: {:error, :oops}
我们可以对列表进行模式匹配
iex> [a, b, c] = [1, 2, 3]
[1, 2, 3]
iex> a
1
列表也支持对其自身的头部和尾部进行匹配
iex> [head | tail] = [1, 2, 3]
[1, 2, 3]
iex> head
1
iex> tail
[2, 3]
类似于 hd/1
和 tl/1
函数,我们无法用头部和尾部模式匹配空列表
iex> [head | tail] = []
** (MatchError) no match of right hand side value: []
[head | tail]
格式不仅用于模式匹配,还用于将项目添加到列表的开头
iex> list = [1, 2, 3]
[1, 2, 3]
iex> [0 | list]
[0, 1, 2, 3]
模式匹配允许开发人员轻松地解构元组和列表等数据类型。正如我们将在接下来的章节中看到的,它是 Elixir 中递归的基础之一,并适用于其他类型,如映射和二进制。
固定运算符
Elixir 中的变量可以重新绑定
iex> x = 1
1
iex> x = 2
2
但是,有时我们不希望变量被重新绑定。
当您希望根据变量的 *现有值* 而不是重新绑定变量来进行模式匹配时,请使用固定运算符 ^
。
iex> x = 1
1
iex> ^x = 2
** (MatchError) no match of right hand side value: 2
由于我们在 x
绑定到 1
的值时固定了它,因此它等效于以下内容
iex> 1 = 2
** (MatchError) no match of right hand side value: 2
请注意,我们甚至看到了完全相同的错误消息。
我们可以在其他模式匹配中使用固定运算符,例如元组或列表
iex> x = 1
1
iex> [^x, 2, 3] = [1, 2, 3]
[1, 2, 3]
iex> {y, ^x} = {2, 1}
{2, 1}
iex> y
2
iex> {y, ^x} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}
由于在固定 x
时它绑定到 1
的值,因此最后一个示例可以写成
iex> {y, 1} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}
如果在一个模式中多次提到一个变量,则所有引用都必须绑定到相同的值
iex> {x, x} = {1, 1}
{1, 1}
iex> {x, x} = {1, 2}
** (MatchError) no match of right hand side value: {1, 2}
在某些情况下,您不关心模式中的特定值。常见的做法是将这些值绑定到下划线 _
。例如,如果我们只关心列表的头部,我们可以将尾部分配给下划线
iex> [head | _] = [1, 2, 3]
[1, 2, 3]
iex> head
1
变量 _
非常特殊,因为它永远无法从中读取。尝试从中读取会引发编译错误
iex> _
** (CompileError) iex:1: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions
尽管模式匹配允许我们构建强大的结构,但它的用法是有限的。例如,您无法在匹配的左侧进行函数调用。以下示例无效
iex> length([1, [2], 3]) = 3
** (CompileError) iex:1: cannot invoke remote function :erlang.length/1 inside match
这结束了我们对模式匹配的介绍。正如我们将在下一章中看到的,模式匹配在许多语言结构中非常常见,它们可以通过保护进一步增强。