檢視原始碼 模式比對
在本章節中,我們將會學習為什麼 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
這結束了我們對模式比對的介紹。正如我們將在下一章中看到的,模式比對在許多語言結構中非常常見,它們可以進一步透過防護條件來擴充。