4-模式匹配

StraightDave發表於2014-09-09

4-模式匹配

匹配運算子
模式匹配
pin運算子

本章起教程進入不那麼基礎的階段,開始涉及函數語言程式設計概念。 在Elixir中,=運算子實際上是一個匹配運算子。 本章將講解如何使用=運算子來對資料結構進行模式匹配。最後本章還會講解pin運算子^,用來訪問某變數之前繫結的值。

4.1-匹配運算子

我們已經多次使用=符號進行變數的賦值操作:

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是一個合法的表示式。由於前面給x賦值為1,左右相同,所以它匹配成功了。而兩側不匹配的時候,MatchError將被丟擲。

變數只有在匹配操作符=的左側時才被賦值:

iex> 1 = unknown
** (RuntimeError) undefined function: unknown/0

錯誤原因是unknown變數沒有被賦過值,Elixir猜你想呼叫一個名叫unknown/0的函式,但是找不到這樣的函式。

4.2-模式匹配

匹配運算子不光可以匹配簡單數值,還能用來解構複雜的資料型別。
例如,我們在元組上使用模式匹配:

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", "!"]
** (MatchError) no match of right hand side value: [:hello, "world", "!"]

有趣的是,我們可以匹配特定值。下面例子中匹配的左端當且僅當右端是個元組,且第一個元素是原子: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

列表支援匹配自己的head和tail(這相當於同時呼叫hd/1和tl/1,給head和tail賦值):

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的一個基礎,對其它資料結構同樣適用,比如圖和二進位制。

4.3-pin運算子

在Elixir中,變數可以被重新繫結:

iex> x = 1
1
iex> x = 2
2

如果你不想這樣,可以使用pin運算子(^)。加上了pin運算子的變數,在匹配時使用的值是匹配前就賦予的值:

iex> x = 1
1
iex> ^x = 2
** (MatchError) no match of right hand side value: 2
iex> {x, ^x} = {2, 1}
{2, 1}
iex> x
2

注意如果一個變數在匹配中被引用超過一次,所有的引用都應該繫結同一個模式:

iex> {x, x} = {1, 1}
1
iex> {x, x} = {1, 2}
** (MatchError) no match of right hand side value: {1, 2}

有些時候,你並不在意模式裡的一些值。通常你就可以把它們繫結到特殊的變數“_”上。例如,如果你只想要某列表的head,而不要tail值。你可以這麼做:

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: illegal pattern

相關文章