Erlangのパターンマッチング
先日、「すでに"束縛されている変数"でパターンマッチングができる」と書いたとこにコメントをいただいたので補足。
Erlangでは、変数は1度しか束縛できない。
値を一度しか代入できない。
なので当然、変数Xに対して、次のように
まず1を代入、
次に2を代入、
としようとすると、エラーとなる。
50> X = 1. 1 51> X = 2. =ERROR REPORT==== 19-Mar-2008::01:28:30 === Error in process <0.114.0> with exit value: {{badmatch,2},[{erl_eval,expr,3}]} ** exited: {{badmatch,2},[{erl_eval,expr,3}]} **
だけど、変数に、同じ値を代入すれば、2度目の代入でもエラーにならない。
45> X = 1. 1 46> X = 1. 1 47> X = 2 - 1. 1 48> X = 2. =ERROR REPORT==== 19-Mar-2008::01:24:03 === Error in process <0.106.0> with exit value: {{badmatch,2},[{erl_eval,expr,3}]} ** exited: {{badmatch,2},[{erl_eval,expr,3}]} **
これは、Erlangの=が、よくある代入を意味するのではなく、
ある規則でパターンマッチングと束縛を行うからだ。
X = 1などとしたとき、
変数Xが、未束縛だったら、どんなものにもマッチして、その値に束縛される。
このとき当然パターンマッチに失敗することはない。
もし変数Xが、1に束縛済みだったら、
X = 1 は1 = 1と同じことになり、パターンマッチに成功する。
X = 2 は2 = 1となって、パターンマッチに失敗する。
さらに例を挙げると、X = 1と束縛されていて、Yが未束縛だったら、
{X, Y} = {2, 3}
だとマッチしないが、
{X, Y} = {1, 5}
だとマッチして、変数Yが値5に束縛される。
で、この仕組みが、receive文で便利に使えるもよう。
erlangでは、メッセージを受け取るためにreceive文がある。
receiveでは、関数の引数と同じようにパターンマッチをさせることができる。
で、パターンマッチの中で、関数の引数などのすでに束縛されている変数を使うことができる。以下例。
-module(mod). -export([f/1]). f(X) -> receive {X, Y} -> io:fwrite("ok ~w~n",[Y]); Any -> io:fwrite("error ~w~n",[Any]) end, f(X).
3> c(mod). {ok,mod} 4> F1 = spawn(fun() -> mod:f(1) end). <0.46.0> 5> F7 = spawn(fun() -> mod:f(7) end). <0.48.0> 6> F1 ! {1, 99}. ok 99 {1,99} 7> F1 ! {2, 99}. error {2,99} {2,99} 8> F1 ! {7, 99}. error {7,99} {7,99} 9> F7 ! {7, 99}. ok 99 {7,99} 10> F7 ! {8, 99}. error {8,99} {8,99}
とか、
-module(mod). -export([g/1]). g(X) -> receive {set, Y} -> io:fwrite("set~n"),g(Y); X -> io:fwrite("yes~n"),g(X); Any -> io:fwrite("no~n"),g(X) end.
64> c(mod). ./mod.erl:15: Warning: variable 'Any' is unused {ok,mod} 65> G = spawn(fun() -> mod:g(1) end). <0.153.0> 66> G!1. yes 1 67> G!2. no 2 68> G!{set,2}. set {set,2} 69> G!1. no 1 70> G!2. yes 2
とか、
h() -> receive X -> io:fwrite("set~n") end, receive X -> io:fwrite("yes~n"); Any -> io:fwrite("no~n") end, h().
79> H = spawn(fun mod:h/0). <0.177.0> 80> H!1. set 1 81> H!1. yes 1 82> H!2. set 2 83> H!3. no 3 84> H!4. set 4 85> H!4. yes 4
とか
-module(mod2). -export([get/1]). get(X) -> receive {X, Y} -> Y end.
5> c(mod2). {ok,mod2} 6> self()!{joe, "Hello!"}. {joe,"Hello!"} 7> self()!{guido, "Python!"}. {guido,"Python!"} 8> self()!{joe, "Erlang!"}. {joe,"Erlang!"} 9> self()!{guido, "GJ!"}. {guido,"GJ!"} 10> mod2:get(guido). "Python!" 11> mod2:get(guido). "GJ!" 12> mod2:get(joe). "Hello!" 13> mod2:get(joe). "Erlang!"
のようなことができる。面白い。