Erlang 基礎ポイント3 - case式, if式 例外
case式
Erlang 基礎ポイント2 - DukeLabでパターン集合について説明しました。
パターン集合の結果から処理を複数の関数に分けることができましたが、
関数の数が無駄に多くなる恐れがありますね。
もちろん、プログラムの処理は可能な限り小さくした方が
モジュール化側面でいいでしょうが、やりすぎると、
かえって複雑なプログラムになりかねないですね。
case式を使うと、一つの関数内で処理部を分けておくことができます。
Javaのswitch caseのようなものですが、判断条件に固定値だけでなく、パターンが使えるので、
もっと強力です。
まず、例をみてみましょう。
前のguard_module.erlがErlang 基礎ポイント2 - DukeLabのパターン集合節で紹介したもので
後のcase_test.erlがcase式を使うように修正したものです。
% guard_module.erl -module(guard_module). -export([guard_test/1]). guard_test(X) when X < 5, X > 1 -> X * X; guard_test(X) when X >= 5; X < 10 -> X - 2; guard_test(X) -> X - 2. % case_test.erl -module(case_test). -export([guard_test/1]). guard_test(X) -> case X of X when X < 5, X > 1 -> X * X; X when X >= 5; X < 10 -> X - 2; X -> X - 2 end.
caseの使い方は次のようです。
他のプログラミングの経験があるなら、問題ないでしょう。
case 評価対象 of パターン1 [when ガード...] -> 結果; ... パターンn [when ガード...] -> 結果 end
最後のパターンにはセミコロンをつけないことに注意して下さい。
評価対象に判断対象(関数引数・変数など)を指定し、
パターンnに判断対象と照合するパターンを書きます。
必要ならガードに追加条件を指定できますが、ガードは省略可能です。
評価対象がパターンに一致すれば、結果が返されます。
一致するパターンがないと、例外が発生するので、注意しましょう。
caseの方が関数の長さが増えましたが、関数の数は減りましたね。
どちらも優先的に使うのではなく、関数の数とcaseの数をバランスよく
配置すればいいと思います。
if式
他のプログラミング言語のif文と同じですが、違いは式なので、戻り値があるということです。
上記のcase式のサンプルをif式に修正したものを載せます。
% if_test.erl -module(if_test). -export([if_test/1]). if_test(X) -> if X < 5, X > 1 -> X * X; X >= 5, X < 10 -> X - 2; true -> X - 3; end. % if_testをREPL環境で試す。 19> c(if_test). {ok,if_test} 20> if_test:if_test(7). 5 21> c(if_test). {ok,if_test} 22> if_test:if_test(3). 9 23> if_test:if_test(6). 4 24> if_test:if_test(-1). -4
if式の文法は他のプログラミング言語のif文と似ていますが、ちょっと違いますね。
まず、複数個の条件を書く時、ifと複数書く必要がありません。
最初にifと書いて、case式のように条件と結果を次々と書いていけばいいです。
それから、最後の条件にtrueと書きましたね。これはelseと考えればいいです。
Erlangのifにはelseがありません。なので、全ての条件に一致しない時の条件を
直接書く必要がありますが、trueと書いておけばOKです。
case 評価対象 -> パターン1 [when ガード...] -> 結果; ... パターンn [when ガード...] -> 結果 end
trueを書かないで、全ての条件に一致しない場合、例外が発生しますので、注意しましょう。
例外
Erlangで例外処理は、ちょっと形式は違いますが、Javaと同じくtry-catch式を使います。
以下の例を見ながら、try-catchを身につけましょう。
% trycatch.erl -module(trycatch). -export([go/1]). go(X) -> try test(X) of Val -> {value, Val} catch throw:exception1 -> io:format("Catch Exception_Test1~n"); throw:exception2 -> io:format("Catch Exception_Test2~n"); exit:_ -> io:format("Catch Exit~n"); error:_ -> io:format("Catch Error~n") after io:format("After~n") end . test(X) -> case X of 1 -> throw(exception1); 2 -> throw(exception2); 3 -> exit(unknown); 4 -> error(unknown) end . % trycatchをREPL環境で試す。 1> c(trycatch). {ok,trycatch} 2> trycatch:go(1). Catch Exception_Test1 After ok 3> trycatch:go(2). Catch Exception_Test2 After ok 4> trycatch:go(3). Catch Exit After ok 5> trycatch:go(4). Catch Error After ok
関数goに1から4までの数字を入れると、例外(throw)2種類、プロセス終了(exit)1種類、エラー(error)1種類を演じてくれます。
throwは呼出元が例外を処理することを期待する時、呼出元に対してエラーをなげる関数です。JavaのChecked Exceptionと似ていますね。
但し、throwでエラーを発生させても、呼出元がtry-catchでエラーを捉えなくても実行はできます。
Javaのようにコンパイルエラーは発生しません。
プログラムで捉えられなかったエラーはErlangが捉えます。その場合、スタックトレースのようなものが表示されるでしょう。
exitはプロセスを終了させたい時、呼出元にエラーをなげる関数です。try-catchでこのエラーを捉えないと、プロセスが終了してしまいます。
errorは呼出元が例外を処理できないような致命的なエラーが発生したことを知らせるため関数です。
Javaでいうと、RuntimeExceptionのようなものでしょうか。
よく見るとErlangのtry-catchはcaseと似ていることが分かります。
tryとcatchの間のコードがまさにcaseのような形式、その後、catchとafterが追加されていますね。
catchはJavaのcatchと同じで、afterはJavaのfinallyのようなものです。
以下はtry-catch式の使い方です。
try 評価対象 of パターン1 [when ガード...] -> 結果; ... パターンn [when ガード...] -> 結果 catch 例外種類1(throw, exit, error): 例外パターン1 [when ガード1] -> 結果1; ... 例外種類n(throw, exit, error): 例外パターンn [when ガードn] -> 結果n after catch結果に関係なく、返す結果 end
Erlangのtry-catchはJavaと違って、式なので戻り値があります。
今日はここまで。
次はErlangでの並行処理について書きます。