DEV Community

gumi TECH for gumi TECH Blog

Posted on • Edited on

Elixir入門 04: パターンマッチング

本稿はElixir公式サイトの許諾を得て「Pattern matching」の解説にもとづき、加筆補正を加えてElixirの演算子による基本的なパターンマッチングについてご説明します。

マッチ演算子

演算子=/2は変数に値を代入するために用いることができます。

iex> x = 1
1
iex> x
1
Enter fullscreen mode Exit fullscreen mode

=/2のElixirにおける呼び名はマッチ演算子です。左オペランドの値が右オペランドと合致(マッチ)するかを試します。マッチすればその値が返され、しないときはエラーになるのです。左オペランドが変数の場合には、右オペランドの値が代入されます。

iex> x = 2
2
iex> 2 = x
2
iex> 1 = x
** (MatchError) no match of right hand side value: 2
Enter fullscreen mode Exit fullscreen mode

右オペランドに未定義の変数があると、その名前のアリティ0の関数を探して呼び出そうとします。そして、見つからなければエラーになるのです。

iex> 1 = unknown
** (CompileError) iex:1: undefined function unknown/0
Enter fullscreen mode Exit fullscreen mode

演算子によるパターンマッチング

複数のデータをもつリストやタプルにも、マッチ演算子=は使えます。

iex> list = [1, 2, 3]
[1, 2, 3]
iex> [1, 2, 3] = list
[1, 2, 3]
iex> [] = list
** (MatchError) no match of right hand side value: [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

=演算子でリストの各要素を変数に取り出すこともできます。ただし、リストの長さもマッチしなければなりません。

iex> [a, b, c] = list
[1, 2, 3]
iex> a
1
iex> b
2
iex> c
3
iex> [a, b] = list
** (MatchError) no match of right hand side value: [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

|演算子を用いたパターンマッチングにより、ヘッドとテイルをふたつの変数に与えることもできます。ただし、関数hd/1tl/1と同じように、空のリストはマッチさせられません。

iex> [head | tail] = list
[1, 2, 3]
iex> [2, 3] = tail
[2, 3]
iex> [1] = head
** (MatchError) no match of right hand side value: 1
iex> 1 = head
1
iex> [h | t] = []
** (MatchError) no match of right hand side value: []
Enter fullscreen mode Exit fullscreen mode

|演算子は、別のリストを後につなげるために用いることもできます。

iex> list = [1, 2, 3]
[1, 2, 3]
iex> [0 | list]
[0, 1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

タプルの要素も、マッチ演算子=で取り出せます。もちろん、要素の数はマッチしなければなりません。

iex> {a, b, c} = {:hello, "world", 2018}
{:hello, "world", 2018}
iex> a
:hello
iex> c
2018
iex(21)> {a, b} = {:hello, "world", 2018}
** (MatchError) no match of right hand side value: {:hello, "world", 2018}
Enter fullscreen mode Exit fullscreen mode

両オペランドの型が異なると、マッチせずにエラーになります。

iex> {a, b, c} = [:hello, "world", 42]
** (MatchError) no match of right hand side value: [:hello, "world", 42]
Enter fullscreen mode Exit fullscreen mode

左オペランドの要素の一部に値を与えることもできます。すると、その要素の値もマッチしなければならないのです。

iex> {:ok, result} = {:ok, 1}
{:ok, 1}
iex> result
1
iex> {:ok, result} = {:error, :oops}
** (MatchError) no match of right hand side value: {:error, :oops}
Enter fullscreen mode Exit fullscreen mode

ピン演算子

=演算子の左オペランドが変数の場合、右オペランドの値が代入されます。けれど、左オペランドの変数にピン演算子^/1を添えると、右オペランドとのマッチが求められるのです。

iex> x = 1
1
iex> x = 2
2
iex> ^x = 1
** (MatchError) no match of right hand side value: 1
iex> ^x = 2
2
Enter fullscreen mode Exit fullscreen mode

^/1演算子はタプルにも使えます。左オペランドの^/1がついた変数は今もっている値に固定され、右オペランドの対応する要素がマッチしなければならなくなるのです。

iex> {x, y} = {2, 1}
{2, 1}
iex> {x, y} = {1, 2}
{1, 2}
iex> {^x, y} = {1, 3}
{1, 3}
iex> {^x, y} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}
iex> x
1
iex> y
3
Enter fullscreen mode Exit fullscreen mode

=演算子の左オペランドのパターンに同じ変数が複数ある場合、それらはすべて同じパターンを参照します。そのため、同じ変数に異なる値は代入できません。けれど、同じ変数がふたつのとき、ひとつに^/1演算子を添えれば、パターンとして参照されるのは今の値です。したがって、^/1をつけた変数に対応する右オペランドの要素がその値とマッチすれば、もうひとつの変数に別の値を与えられます。

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

パターンの中の変数で値を問わない場合があるでしょう。そのときに用いるのがアンダースコア(_)です。

iex> [h | _] = [1, 2, 3]
[1, 2, 3]
iex> h
1
Enter fullscreen mode Exit fullscreen mode

_は特別な変数です。値を取り出すことはできず、エラーになります。

iex> _
** (CompileError) iex:n: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions
Enter fullscreen mode Exit fullscreen mode

Elixir入門もくじ

番外

Top comments (0)