束縛済み変数でパターンマッチングの続き
Erlangは絶賛勉強中であり、また新たな事実が発見された。
Erlangではマッチングに使うためのパターンを、無名関数でも複数書くことができる。
あと、束縛済み変数をマッチングのパターンに便利に使えるのは、新たにスコープが生成されないで複数のパターンが書けるときで、
それはreceive文しかないと思ったが、case文でも同じことができた。
説明不足で何を言っているか分からんという感じだと思われるので、Haskellの場合と比較しながらコードを書いてみる。
Haskellでcase文。
f x y = case x of y -> True _ -> False
case文は独立したスコープを持つ。それにそもそも遅延評価が基本で、
Erlangのように逐次実行でだらだらと順番に束縛していく、ということはありえない。
case文のパターンに現れてるyは、外側の関数fの引数のyとは当然違う。
なので、fのcase文は最初のパターンが必ずマッチし、戻り値は必ずTrueになる。
遅延評価なので、fの引数は評価すらされない。
Main> f 1 2 True Main> f 2 2 True Main> f 4 3 True Main> f (error "") (error "") True
Erlangでは、case文は独立したスコープを持たない。
case文の一つ目のパターンにある変数Yは、外側の関数fの引数にすでに束縛されている。
-module(md2). -export([f/2]). f(X, Y) -> case X of Y -> true; _ -> false end.
なので、case文の一つ目のパターンは、マッチしたりしなかったりする。
3> c(md2). {ok,md2} 4> md2:f(1,2). false 5> md2:f(2,2). true
無名関数の場合は、独立したスコープを持ち、引数となっている変数名は、外側の環境の同じ名前の変数名を隠蔽して、
あらたな未束縛の変数扱いになる。なので、
-module(md3). -export([f/1,g/1,g2/1,h/1]). f(X) -> fun (X) -> true; (_) -> false end.
上記のような定義だと、一つ目のパターンに必ずマッチする。
19> (md3:f(1))(2). true 20> (md3:f(1))(1). true
ガードを使えばもちろんできる。が、雑然とした感じ。これができるだけでも十分便利だが。
g(X) -> fun (Y) when Y =:= X -> true; (_) -> false end.
これだとマッチしたりしなかったり、が実現できる。
21> (md3:g(1))(2). false 22> (md3:g(1))(1). true
case文をつかうと、入れ子になってしまうが、ガードを使わなくてすむ。
g2(X) -> fun(Y) -> case Y of X -> true; _ -> false end end.
動きは同じ。
21> (md3:g2(1))(2). false 22> (md3:g2(1))(1). true
次のような場合、Xに関しては隠蔽が起こらない。
ので、X=Yのところで、マッチに失敗しうる。
失敗すると、badmatchってことになる。
h(X) -> fun (Y) -> X = Y; (_) -> false end.
23> (md3:h(1))(1). 1 24> (md3:h(1))(2). =ERROR REPORT==== 20-Mar-2008::00:41:26 === Error in process <0.30.0> with exit value: {{badmatch,2},[{md3,'-h/1-fun-0-',2}, {shell,exprs,6},{shell,eval_loop,3}]} ** exited: {{badmatch,2}, [{md3,'-h/1-fun-0-',2},{shell,exprs,6},{shell,eval_loop,3}]} **
Erlangの変数の仕組みは、癖があるけど面白い。
こういうタイプの言語って他にもあるんだろうか?