Erlangの入門記事 パターンマッチについて

パターンマッチ

 Erlangを理解するためには、パターンマッチを理解しなければなりません。まずは、JavaScriptのswitchに似ているErlangのcaseからです。

例1

case X of
   123 -> a;
   "abc" -> b;
   [1,2] -> c
 end

Xが123の時にはaを返し、
Xが"abc"の時にはbを返し、
Xがリストの[1,2]であればcになります。
もし、Xがこれら以外であれば例外が発生します。

例2

 case X of
   [1,2,A] -> A;
   [3,A,B] -> A+B
 end

上記の例は、 Xが[1,2,3]なら3,[1,2,5]なら5 というふうに、[1,2]で始まる要素3のリストであればマッチし、3つ目の要素が変数Aにバインドされます。
Xが[3]から始まる要素3のリストであれば、[3,4,5]なら9、[3,5,10]なら15というふうに、2番めの要素がAに、3番目の要素がBにバインドされて処理されます。

Javaには無い特性として、単なる数値や文字列との比較だけでなく、リストやタプルなどの比較や、変数へのバインドができます。

例3

A=5
 case X of
   [1,2,A] -> A;
   [3,A,B] -> A+B
 end

先ほどの例にそっくりですが、予めAに5がバインドされています。この場合は、Xは[1,2,5]の時のみ[1,2,A]にマッチします。同じように[3,A,B]は、[3,5,B]となり、例2ではマッチした[3,4,5]ではマッチしなくなります。

=は代入ではない

JavaScriptでは=で変数に代入できます。JavaScriptでは、A=1;A=2; のように何度でも代入できますが、Erlangでは一度しか代入できません。というか、Erlangでは=は代入ではないのです。

= は、実はパターンマッチで、caseの特殊な形と考えるとわかりやすくなります。

A = B.

は、

case B of
  A -> A
end.

なのです。

ですから、一旦Aが何らかの値にバインドされてしまうと、2回目以降はパターンマッチが成功して値が返されるか、例外が起きるかするのです。

繰り返しますが、=はパターンマッチです。ですからJavaScriptでは見慣れない不思議な代入文が見られます。例えば、

{ok,Bin} = file:read_file("data.bin")

これは、ファイルの読み込みが成功した時のみBinにファイルの内容がバインドされ、失敗した場合はパターンマッチに失敗して例外が発生します。

関数定義もパターンマッチ

Fibonacci数列の計算を例にあげます。

JavaScriptでは、

function fib(n) {
    return n < 2 ? n : fib(n - 2) + fib(n - 1);
}

すごく素直に書けば、

function fib(n) {
    if(n==0) return 0;
    else if(n==1) return 1;
    else return fib(n - 2) + fib(n - 1);
}

のように書けるでしょうか。 これは、fib関数のパラメーターが0の時は0、1の時は1、それ以外であればfib(n - 2) + fib(n - 1)というパターンと考えられます。

Erlangでは

fib(0)->0;
fib(1)->1;
fib(N)->fib(N-2)+fib(N-1).

と書くことができます。関数名とそのパラメーターに対する挙動のパターンの定義が関数定義であると考えることができます。

他にも条件分岐のif、メッセージを受け取るreceiveでも -> がでてきます。-> は、あるパターンに対する挙動を記述するパターンマッチの記述なのです。