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でも -> がでてきます。-> は、あるパターンに対する挙動を記述するパターンマッチの記述なのです。