模式匹配

在本章中,我们将展示=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/1tl/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

这完成了我们对模式匹配的介绍。正如我们将在下一章中看到的,模式匹配在许多语言结构中很常见。