次へ 前へ 目次へ 佐々木将人の個人ページへ

8 Erlangにおけるパターンマッチの基礎

 数学では「=」は「=」の左側(左辺)と「=」の右側(右辺)が等しいという意味で用います。
 一方,BASICやCを起源とする言語を筆頭に,「=」は,左辺の変数に右辺の値を代入するという意味で用います。
 たいていは算数で等号としての「=」を先に習うので,左辺の変数に右辺の値を代入するという(例えば)X = X + 2 のようなコードは違和感がある,理解できないとしてつまづく人は一定数いると思います。
 そしてErlangでは,さらに複雑な意味になります。

単純な形の=の動作

 まず,右辺の値を求めます。
 右辺に変数があって,しかも束縛されていないような場合には,直ちに変数が束縛されていない旨のエラーを返します。
 次に,左辺に変数があるか,変数がある場合に,束縛されているかどうかを調べます。
 もし変数があって,束縛されていない場合には,右辺の値をその変数に束縛します。束縛した後は,束縛された変数として扱います。
 ここまでくると結果的に左辺の変数は必ず束縛されているわけですから,その値が右辺と異なるかどうかを調べます。
 束縛されていないとして右辺の値で束縛した場合はもちろんのこと,最初から右辺の値で束縛されていた場合でも,右辺の値と同じであればパターンマッチ成功で,その値を返します。
 もし右辺と左辺の値が異なればパターンマッチ失敗です。
 変数がない場合も同様で,左辺と右辺の値が等しければ,パターンマッチ成功で,その値を返し,左辺と右辺の値が異なれば,パターンマッチ失敗でエラーを返します。
 なお,左辺が単なる変数だけではなく,「X + 3」のような形式(数学でいう多項式)の場合には,右辺が求められている時に(数学的には単項式),パターンマッチは失敗します,
 また,「_」(アンダースコア)で始まる変数は,パターンマッチとしては常に成功する代わりに,1回でそのことを忘れてしまうという習性があります。そこで,なんにでもパターンマッチできて,しかも一時的な変数として使いまわしができる,ワイルドカード的な使い方ができます。

 実際に見てみましょう。(なお,「%」から行末まではコメントです。ソースにあっても無視されます。)

1> X .
* 1:1: variable 'X' is unbound % 変数 X は束縛されていないので,束縛されていないというエラーを返します。
2> X = 2 .
2 % 変数 X は束縛されていませんので,右辺の数値2が束縛されます。そして,束縛の結果,このパターンマッチは成功したので,値2を返します。
3> X = 2 .
2 % 変数 X は数値2と束縛されていますが,右辺の数値2と等しいので,パターンマッチが成功し,値2を返します。
4> X = Y + 1 .
* 1:5: variable 'Y' is unbound % まだ変数 Y は束縛されていないので,その旨エラーを返します。
5> Y = 2 .
2 % 変数 Y に数値2を束縛します。パターンマッチは直ちに成功し,数値2を返します。
6> X = Y + 1 .
** exception error: no match of right hand side value 3 % 左辺が2,右辺が3ですので,パターンマッチが失敗し,例外を返しています。
7> Z - 3 = X .
* 1:3: illegal pattern % 変数 Z は束縛されていないのですが,左辺が多項式,右辺が単項式というので,パターンマッチが失敗します。
8> _ = 2 .
2 % 一時的変数 _ に数値2が束縛され,パターンマッチが直ちに成功し,数値2を返します。しかし,この束縛はすぐに忘れてしまいますので……
9> X = _ .
* 1:5: variable '_' is unbound % 変数 _ は束縛されていないというエラーを返します。
10> _ = X .
2 % 一時的変数 _ に右辺の値である変数 X の値である数値2が束縛され,パターンマッチが直ちに成功し,数値2を返します。そしてこの束縛はすぐに忘れてしまいます。
11> _ .
* 1:1: variable '_' is unbound

タプルの場合の=の動作

 タプルというのは複数の要素を {} で囲んだものでした。
 タプルについては,まず要素数が等しい上で,各要素が上の単純な場合のルールにしたがってパターンマッチすることが要求されます。
 変数をうまく組み合わせると,タプルの中の各要素を取り出すことができます。

1> {2,3} = {2} .
** exception error: no match of right hand side value {2} % タプルは要素数が違うとパターンマッチに失敗します。
2> {2,3} = {2,3} .
{2,3} % 要素数が同じなのでパターンマッチに成功して,値である{2,3}のタプルを返します。
3> {2,3} = {2,3,4} .
** exception error: no match of right hand side value {2,3,4} % タプルは要素数が違うとパターンマッチに失敗します。
4> {X,Y} = {2,3} .
{2,3} % 要素数が同じであるので,各要素について上の単純な場合のルールにしたがって,X に数値 2 を,Y に数値 3 を束縛します。
5> X .
2 % 変数 X に数値2が束縛されていることの確認です。
6> Y .
3 % 同様に変数 Y に数値3が束縛されていることの確認です。
7> {X,Y} = {2,4} .
** exception error: no match of right hand side value {2,4} % 変数 X には数値2が,変数 Y には数値3がそれぞれ束縛されているので,異なる数値を束縛しようとすると例外が返されます。
8> {X,Y} = {3,3} .
** exception error: no match of right hand side value {3,3}
9> {2,3} = {Z,U} .
* 1:10: variable 'Z' is unbound % 右辺の変数に左辺の値を束縛することはできず,右辺の変数が束縛されていないというエラーを返します。
10> {Z,_} = {4,5} .
{4,5}
11> Z .
4
12> _ .
* 1:1: variable '_' is unbound % 「_」で始まる一時的変数は,束縛されたことをすぐに忘れてしまうのでした。

リストの場合の=の動作

 =はリストでもタプルの時と同様の動作をします。すなわち要素数が等しいことを確認した上で,各要素についてパターンマッチを試みます。
1> [1,2,3] .
[1,2,3]
2> [X,Y,Z] = [1,2,3] .
[1,2,3]
3> X .
1
4> Y .
2
5> Z .
3

 リスト内包表記でも同じです。

6> [2,4,6] = [ 2 * X || X <- [1,2,3] ] .
[2,4,6]
 cons演算子を活用することで,carとcdrを取り出すことも可能になります。
7> [Head | Tail] = [1,2,3] .
[1,2,3] % 値としては右辺のリストを返します。
8> Head .
1 % 右辺のリストのhdすなわちLispでいうcarの値を変数Headに束縛しています。
9> Tail .
[2,3]% 右辺のリストのtlすなわちLispでいうcdrの値を変数Tailに束縛しています。

(2023.5.21. 初版)

次へ 前へ 目次へ 佐々木将人の個人ページへ