URL
試した環境
- Ubuntu Server 14.04 LTS
- Erlang/OTP 18
- Elixir 1.0.4
Pattern matching
The match operator
```sh
iex> x = 1
1
iex> x
1
実際には、=
はマッチ演算子と呼ばれる。
iex> 1 = x
1
iex> 2 = x
** (MatchError) no match of right hand side value: 1
1 = x
は有効な式で、左辺の値と右辺の値が等価なため、マッチする。両辺の値が一致しない場合は、MatchErrorが起きる。
左辺にないと変数に値を割り当てることはできない。
iex> 1 = unknown
** (RuntimeError) undefined function: unknown/0
上の処理は、変数unknownが定義されているわけではないので、unknown/0という名前の関数の呼び出しをしたが、そのような関数はないのでエラーになっている。
Pattern matching
マッチ演算子は、単純な値に対してマッチする処理だけに使われるわけではない。より複雑なデータ型の値の取り出しに有用。
例えば、タプルのパターンマッチに使える。
iex> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}
iex> a
:hello
iex> b
"world"
iex> c
42
パターンマッチは、両辺がマッチしなければerrorになる。
例えば、タプルのサイズが違う場合などはMatchErrorになる。
iex> {a, b, c} = {:hello, "world"}
** (MatchError) no match of right hand side value: {:hello, "world"}
異なる型を比較した場合もMatchErrorになる。
iex> {a, b, c} = [:hello, "world", "!"]
** (MatchError) no match of right hand side value: [:hello, "world", "!"]
さらに、特定の値をマッチさせることができる。
次の例は、左辺が:okというatomで始まるタプルということが言えるマッチです。
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(3)> [a, b, c] = [1, 2, 3]
[1, 2, 3]
iex> a
1
iex> b
2
iex> c
3
リストは、自身のheadとtailのマッチングもサポートしている。
iex(7)> [head | tail] = [1, 2, 3]
[1, 2, 3]
iex(8)> head
1
iex(9)> tail
[2, 3]
hd/1やtl/1関数と同じように、headとtailのパターンマッチは空リストに対しては使えない。
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]
パターンマッチングは、タプルやリストなどのデータ型の構造を簡単に分解する方法を提供する。再帰処理の基板であり、mapsやバイナリのような他の型にも適用できる。
The pin operator
Elixirでは変数は再束縛できる。
iex > x = 1
1
iex > x = 2
2
ここは少し気になった。
Erlangでは変数は単一代入なので再束縛できない。
飛行機本では、プログラムが正しくない場合に、変数の値が期待したものでないことがきっかけであることも多いとして、Erlangでは単一代入にすることで、変数の変更される箇所を限定し、デバッグがやりやすいとしていた。
Elixirでは、Erlangの変数の単一代入よりも、他の言語のように再束縛できた方がよいということだろう。
変数のスコープ関数内だけなので、再束縛できてもそこまで問題はないかもしれない。
ただし、ピン演算子^
というものがあり、これは変数の再束縛ではなく、以前にマッチした値とのマッチングをしたいときに使える。
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
上記のサンプルの{x, ^x} = {2, 1}
は、xは2に再束縛され、^x
は以前の値の1としてマッチしている。
そのため、次のような場合は、^x
がマッチしないので失敗する。
iex> x = 1
1
iex> {x, ^x} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}
パターン中に複数回使われている変数は、すべて同じパターンへ束縛される。
iex> {x, x} = {1, 1}
{1, 1}
iex(> {x, x} = {2, 2}
{2, 2}
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:41: unbound variable _
パターンマッチングで強力な構造を構築できるが、使い方に制限がある。例えば、マッチの左辺で関数を呼び出すことはできない。
iex> length([1,2,3]) = 3
** (CompileError) iex:45: illegal pattern