模式匹配 | Pattern matching
模式匹配
在本章中,我们将展示=
Elixir 中的操作符实际上是一个匹配运算符,以及如何使用它来对数据结构内部的匹配进行匹配。最后,我们将学习^
用于访问以前绑定值的 pin 操作符。
匹配运算符
我们已经使用过=
几次操作符来分配 Elixir 中的变量:
iex> x = 1
1
iex> x
1
在 Elixir 中,=
操作员实际上被称为匹配操作员
。让我们看看为什么:
iex> 1 = x
1
iex> 2 = x
** (MatchError) no match of right hand side value: 1
注意这1 = x
是一个有效的表达式,并且它匹配,因为左侧和右侧都等于1.当边不匹配时,MatchError
会引发a。
变量只能分配在左侧=
:
iex> 1 = unknown
** (CompileError) iex:1: undefined function unknown/0
由于unknown
之前没有定义变量,Elixir 想象你正试图调用一个名为的函数unknown/0
,但是这样的函数不存在。
模式匹配
匹配运算符不仅用于匹配简单值,还可用于解构更复杂的数据类型。例如,我们可以在元组上进行模式匹配:
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> [h | t] = []
** (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 中递归的基础之一,也适用于其他类型,如地图和二进制文件。
引脚(pin)操作符
Elixir 中的变量可以被反弹:
iex> x = 1
1
iex> x = 2
2
^
当您想要对现有变量的值进行模式匹配而不是重新绑定变量时,请使用 pin 操作符:
iex> x = 1
1
iex> ^x = 2
** (MatchError) no match of right hand side value: 2
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> [h | _] = [1, 2, 3]
[1, 2, 3]
iex> h
1
该变量_
是特殊的,它永远不会被读取。试图从它读取给出一个未绑定的变量错误:
iex> _
** (CompileError) iex:1: unbound variable _
虽然模式匹配允许我们构建强大的构造,但其用法是有限的。例如,您不能在匹配的左侧进行函数调用。以下示例无效:
iex> length([1, [2], 3]) = 3
** (CompileError) iex:1: cannot invoke remote function :erlang.length/1 inside match
这完成了我们对模式匹配的介绍。正如我们将在下一章中看到的,模式匹配在许多语言结构中很常见。