5. 関数の構文

5.1. パターンマッチ

../_images/snail1.png

いまやコードをコンパイルして保存することが出来るようになったので、もっと複雑な関数を書き始めることができます。 これまで書いてきた関数は究極的に簡単で、ちょっと面白くないものでしたね。 もっと面白いものを作りましょう。最初の関数は相手の性別によって違った風に挨拶する必要があります。 大抵の言語ではこのような形で書くでしょう:

function greet(Gender,Name)
    if Gender == male then
        print("Hello, Mr. %s!", Name)
    else if Gender == female then
        print("Hello, Mrs. %s!", Name)
    else
        print("Hello, %s!", Name)
end

パターンマッチがあるので、Erlangではこう言った決まり文句は省くことが出来ます。Erlangでは同様の関数は次のようになります:

greet(male, Name) ->
    io:format("Hello, Mr. ~s!", [Name]);
greet(female, Name) ->
    io:format("Hello, Mrs. ~s!", [Name]);
greet(_, Name) ->
    io:format("Hello, ~s!", [Name]).

Erlangでは表示関数は他の言語に比べてすごくブサイクだと認めます。しかし、そこは問題ではありません。 ここでの一番の違いは関数のどの部分を使うかとどんな値が必要かをパターンマッチを使って同時に定義したことにあります。 値を束縛してそれを比較する必要などないのです!なのでこういうふうに書く代わりに:

function(Args)
    if X then
Expression
    else if Y then
Expression
    else
Expression

Erlangではこう書きます:

function(X) ->
    Expression;
function(Y) ->
    Expression;
function(_) ->
    Expression.

これで同じような結果が得られますが、ずっと宣言的なスタイルです。 それぞれの関数宣言が関数節と呼ばれています。関数節はセミコロン(;)で区切られ、関数宣言でまとめられます。 関数宣言は一つの大きな文として捉えられ、それが最後の関数節がピリオドで終わっている理由です。 トークンをワークフローの定義に使うのは「おかしい」ですが、慣れるでしょう。 少なくとも、慣れるべきです。なぜならそれ以外に方法はないからです!

Note

io:format のフォーマットは文字列で置き換えられるトークンを使っています。 トークンを記述する際に使う文字はチルダ( ~ )です。 ~n のようないくつかのトークンは組み込みです。 ~n は改行に変換されます。 他のトークンはたいていデータをどのように表示するか記述します。 io:format("~s!~n,["Hello"]).~s というトークンを含んでいて、これは文字列やビット文字列を引数にとります。そして ~n がそれに続きます。 最終的に出力されるメッセージは "Hello!\n" となります。他によく使われるトークンは ~p です。これはErlang項をいい感じに表示してくれます。(全部にインデントを入れてくれます)

io:format 関数は入出力に深く触れるあとの章で詳細を見るとして、しばらくはこれらのどれかを使っていましょう:io:format("~s~n",[<<"Hello">>]), io:format("~p~n",[<<"Hello">>]), io:format("~~~n"), io:format("~f~n", [4.0]), io:format("~30f~n", [4.0]) これで大体この関数ができるすべてのことがわかります。これらは他の多くの言語にある printf 関数のようなものです。 もし入出力の章まで待てなければ オンラインドキュメント を読んでもいいでしょう。

関数内でのパターンマッチはより複雑にかつ強力にできます。ちょっと前の章で触れたんですが、リストのheadとtailを得るためにパターンマッチを使いました。これをやってみましょう! 新しいモジュール functions を始めてみましょう。ここでたくさんの関数を書いてパターンマッチを堪能しましょう:

-module(functions).
-compile(export_all). %% replace with -export() later, for God's sake!

最初に各関数は head/1 です。これは erlang:hd/1 のようにリストを引数として受け取りその最初の要素を返します。 cons演算子を使って次のように書くことが出来ます:

head([H|_]) -> H.

シェルで functions:head([1,2,3,4]). と書けば(モジュールがコンパイル済みの場合)、予想通り ‘1’ が返ってきます。結果として2番目の要素を得るには、次のような関数を書けるでしょう:

second([_,X|_]) -> X.

このリストはパターンマッチのためにErlangによって解体されます。シェルで試してみましょう!

1> c(functions).
{ok, functions}
2> functions:head([1,2,3,4]).
1
3> functions:second([1,2,3,4]).
2

1000までやるのは非実用的ですが、リストに対してやりたいだけこれを繰り返すことができます。 これは再帰関数を書くことで修正することができますが、それについてはあとで触れましょう。 いまはパターンマッチに集中しましょう。 (本当に)始めましょう! で話したように、自由な変数と束縛された変数のコンセプトは関数でも生きています。 関数に渡された2つのパラメータを比較して、それが同じかどうかを知ることができます。これには2つの引数を取って、同じかどうかを教える関数 same/2 を書きましょう:

same(X,X) ->
    true;
same(_,_) ->
    false.

とてもシンプルですね。この関数がどのように動作するか説明する前に、念のため変数の束縛と未束縛に関して再度見てみましょう:

../_images/un-bound1.png

ここで、花婿は悲しがっています。なぜならErlangでは変数は決して値を変更できないからです:自由がない!冗談はさておき、未束縛の変数は値が付いていない変数のことをいいます。(上の絵の右側にあるホームレスのような感じです) 変数を束縛するというのは、単純に値を未束縛の変数にくっつけることを言います。 Erlangでは、束縛されている変数に値を与えようとすると、新しい値が束縛されている値と同じでない限りエラーが発生します。 上の絵の左の男性が双子のどちらかと結婚したとしましょう。もし双子の片割れがきたら、彼は気づかずそのまま過ごすでしょう。 違った女性がきたら、彼は文句を言うでしょう。このへんが曖昧な場合は 変化できない変数 を読んでください。

コードに戻りましょう: same(a,a) を呼び出したときに起きるのは、まず最初のXが未束縛だとみなされます。そして自動的に値aを束縛します。Erlangが次の引数に行ったときに、Xはすでに束縛されていると認識します。そして、2番目の引数に渡された値と比較します。パターンマッチが成功したら関数は true を返します。 もし2つの関数が同じでなかったら、このパターンマッチは失敗となって次の関数節に進みます。2番目の関数節では、引数に何が来ても気にしません。(あなたが行き遅れなら、選り好みしている場合じゃないでしょう!)そして false を返します。 この関数はどんな型のデータでも動作します。リストでも単一の変数でもです。 もうちょっと進んだ例を見てみましょう。次の関数は日付を表示しますが、正しくフォーマットされていなければなりません:

valid_time({Date = {Y,M,D}, Time = {H,Min,S}}) ->
    io:format("The Date tuple (~p) says today is: ~p/~p/~p,~n",[Date,Y,M,D]),
    io:format("The time tuple (~p) indicates: ~p:~p:~p.~n", [Time,H,Min,S]);
valid_time(_) ->
    io:format("Stop feeding me wrong data!~n").

関数の先頭で = 演算子を使って、タプルの中身({Y,M,D})とタプル全体(Date)をパターンマッチ出来ていることに注目してください。この関数は次の用にテストできます:

4> c(functions).
{ok, functions}
5> functions:valid_time({{2011,09,06},{09,04,43}}).
The Date tuple ({2011,9,6}) says today is: 2011/9/6,
The time tuple ({9,4,43}) indicates: 9:4:43.
ok
6> functions:valid_time({{2011,09,06},{09,04}}).
Stop feeding me wrong data!
ok

問題がありました!この関数は値に文字列やアトムでもなんでも受け付けられるます。タプルが {{A,B,C},{D,E,F}} の形をしていれば大丈夫です。 これがパターンマッチの一つの限界を示しています。パターンマッチは、きっちり決まった値、たとえばわかった数のアトムとか、リストのhead|tailなどの絶対的な値、N個の要素のタプル、あるいは任意値( _ や未束縛の変数)です。 この問題を解決するために、ガードを使います。

5.2. ガードだ、ガード!

../_images/driving-age1.png

ガードは関数でより詳細を記述するための追加の節です。 上で書いたように、パターンマッチはある程度制限があって、値の範囲や特定の型のデータを表現することはできません。 表現できない概念は集計です:この12年目の老けたバスケットボール選手はプロでもう活躍できないんでしょうか? この距離は逆立ちで歩くには長すぎるんでしょうか?あなたは車を運転するには若すぎる?あるいは老いすぎてる? あなたは質問には簡単なパターンマッチでは答えられないでしょう。 言いたいことは、例えば車の運転の質問であればこんな感じに書けます:

old_enough(0) -> false;
old_enough(1) -> false;
old_enough(2) -> false;
...
old_enough(14) -> false;
old_enough(15) -> false;
old_enough(_) -> true.

しかしこれは全然使い物になりません。やりたければやってもかまいませんが、あなたは永遠にコードの中でひとりぼっちになりますよ。 ちゃんとお友達を作りたかったら guards モジュールをはじめて、この問題を解決する「正しい」方法を見てみましょう:

old_enough(X) when X >= 16 -> true;
old_enough(_) -> false.

はい、これで終わりです!見て分かるように、ずっと短くてすっきりしています。 ガード表現の基本的なルールは成功時に true を返さなければいけないことです。 もし false が返ってきたらガードは失敗して、例外を投げます。 104歳以上の型が運転するのを禁止してみましょう。ここで運転可能な年齢は16歳から104際になりました。 これを考慮に入れなければいけなんですがどうしたらいいんでしょうか? 2番目のガード節を加えましょう:

right_age(X) when X >= 16, X =< 104 ->
    true;
right_age(_) ->
    false.

カンマ(,)は andalso 演算子と同じように振る舞い、セミコロン(;)は orelse のように振る舞います。 (”(本当に)始めましょう!“で触れています)両方のガード式ともにガード全体で成功しなければいけません。 逆の意味でも関数を表現できます:

wrong_age(X) when X < 16; X > 104 ->
    true;
wrong_age(_) ->
    false.
../_images/guard1.png

これでも正しい結果を取得できます。テストしてみてもいいでしょう(テストは常にすべきです!) ガード式では、セミコロン(;)は orelse 演算子のように動作します。もし最初のガードが失敗したら、2番目のガードを試して、それでもだめなら次、という具合にガードが成功するまで続けて、最後は失敗します。

関数内で比較や真偽をするときにいくつかの関数を使うことが出来ます。数学的表現(A*B/C >= 0)やデータ型に関するもの( is_integer/1, is_atom/1 )があります。(あとの章でこれらについてはやります) ガードの欠点は、副作用を考えてユーザ定義の関数を受け入れないことです。 Erlangは純粋な関数型言語ではありません。( Haskell とは違います) なぜならErlangは多くの副作用に依存しているからです。I/Oもできます。アクター間でメッセージを送ることもできます。 したければエラーを投げることもできます。 ガードの中で使っている関数が文字を表示するかいなか、あるいは重要なエラーをキャッチするのか、その関数が使われてる関数節ごとに定義するのに簡単な方法がありません。 なので代わりにErlangではあなたを信用しないことにしました!(それが正しいと思います!)

こうは言いましたが、ガードが出てきたときに理解できるようにガードの基本的な構文は十分理解すべきです。

Note

ガードでの ,;andalsoorelse と比較しました。 しかしこれらは全く一緒というわけではありません。前者2つは例外をキャッチする一方で、後者2つはしません。 これはつまり、もしガード X >=N; N >= 0 の最初のほうでエラーが投げられても2番目は評価されてガードは成功します。 しかしガード X >= N orelse N >= 0 の最初の部分でエラーが投げられた場合、2番目はスキップされて、ガード全体として失敗します。

しかしながら(常に「しかしながら」があるのですが)、 andalsoorelse だけガードの中で入れ子に出来ます。 つまり (A orelse B) andalso C は適切だということです。 (A; B), C は不適です。 異なった使い方ができるので、一番の戦略は必要に応じて混ぜて使うことです。

5.3. Ifってなんだ!?

if はガードのように振る舞い、ガードと同じような構文を使います。しかし関数節の先頭の外側で使います。 実際、 if 節はガードパターンと呼ばれます。Erlangの if はあなたが他の言語でみてきた if とは異なります。 他の言語での if と比較すると、他の言語での if は違う名前を持った奇妙な怪物のようです。 Erlangの国に入ったなら、今まで if について知っていたことをドアの前に置きさってしまいましょう。 そして席に座って一緒にドライブに行きましょう!

ガードとif式がどれくらい似ているか見てみましょう。次の例を見てください:

-module(what_the_if).
-export([heh_fine/0]).


heh_fine() ->
    if 1 =:= 1 ->
        works
    end,
    if 1 =:= 2; 1 =:= 1 ->
        works
    end,
    if 1 =:= 2, 1 =:= 1 ->
        fails
end.

これを what_the_if.erl として保存して、次を試してみましょう:

1> c(what_the_if).
./what_the_if.erl:12: Warning: no clause will ever match
./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'
{ok,what_the_if}
2> what_the_if:heh_fine().
** exception error: no true branch found when evaluating an if expression
     in function  what_the_if:heh_fine/0
../_images/labyrinth1.png

あらら!コンパイラが、12行目(1 =:= 2, 1 =:= 1)のifは false になってしまうガードしかないのでマッチしないということで警告をだしています。 Erlangでは、すべてのものが何かを返さなければいけません。そして if 式もこの例外ではありません。 このようなことから、Erlangではガードが成功できないとわかったときにはクラッシュします。何かを返せないのです。 したがって、何がっても成功するようにcatch-allを追加する必要があります。 大抵の言語では、これは else と呼ばれています。Erlangでは ‘true’ を使います。 (これがなぜErlang VMがおかしくなったときに”no true branch found”と投げる理由です)

oh_god(N) ->
    if N =:= 2 -> might_succeed;
        true -> always_does  %% this is Erlang's if's 'else!'
    end.

そして、新しい関数をテストすると(古い関数は警告を投げ続けます。無視するか、「すべからず」として残しておきましょう):

3> c(what_the_if).
./what_the_if.erl:12: Warning: no clause will ever match
./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'
{ok,what_the_if}
4> what_the_if:oh_god(2).
might_succeed
5> what_the_if:oh_god(3).
always_does

if式の中でどのようにたくさんのガードを使うかの例となる関数があります。 この関数はどんな式も必ず何かを返さなければいけないということも示しています。 Talkはif式の結果を束縛していて、それをタプル内で文字列と結合しています。 このコードを読んだ時に、 true ブランチがなかった場合、Erlangにはナル値(つまり、LISPでのnil、CでのNULL、PythonでのNoneなどです)のような物はないことを考えれば困ったことになるのがすぐに分かると思います:

%% note, this one would be better as a pattern match in function heads!
%% I'm doing it this way for the sake of the example.
help_me(Animal) ->
    Talk = if Animal == cat  -> "meow";
              Animal == beef -> "mooo";
              Animal == dog  -> "bark";
              Animal == tree -> "bark";
              true -> "fgdadfgna"
           end,
    {Animal, "says " ++ Talk ++ "!"}.

試してみましょう:

6> c(what_the_if).
./what_the_if.erl:12: Warning: no clause will ever match
./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'
{ok,what_the_if}
7> what_the_if:help_me(dog).
{dog,"says bark!"}
8> what_the_if:help_me("it hurts!").
{"it hurts!","says fgdadfgna!"}

あなたもなぜ’else’にかわって’true’になった理由を知りたがっている多くのErlangプログラマの一人でしょう。 結果として、それはとても有名な話になっています。 Richard O’Keefeが次のような回答をErlangメーリングリストに投げました。 うまく表現できないので直接引用します。

Note

とても「有名な」ではありますが、だからと言って’else’はいいとは思いません。 Erlangで’else’と’; true ->’ と書くのはとても簡単なことだと知っていますが、心理的には数十年にわたるプログラミングの結果、それはあまり得策ではないとわかりました。私は次のように置換えをするようになりました:

                by
if X > Y -> a()     if X > Y  -> a()
 ; true  -> b()      ; X =< Y -> b()
end                 end

if X > Y -> a()     if X > Y -> a()
 ; X < Y -> b()      ; X < Y -> b()
 ; true  -> c()      ; X ==Y -> c()
end                 end

この書き方は 書いているときは イライラしますが、 読むとき は本当に助かります。

‘else’ または ‘true’ ブランチは結局のところ「避けられるべき」なのです。 if は通常論理的にすべてのパターンを網羅しておいたほうが catch-all 節に頼るよりも読みやすくなります。

上で述べたように、ガード式内で使える関数は限られています。( でさらに触れます) ここがErlangが魔法を書けられるべき制限がかかった部分です。ここでそれをあなたにあげましょう。 case 式です!

Note

ここで what_the_if.erl 内の関数で示された恐ろしいものは、 if 言語が他の言語での if の立場で書かれた場合、どんな恐ろしいことが起きるか表したものです。 Erlangのコンテキストでは、紛らわしい名前を持た完全に論理的な構造物だとわかりました。

5.4. もしも...の場合 (In Case ... of)

もし if 式がガードのようなら、 case ... of 式は関数のようです。引数を持った複雑なパターンマッチや、その上にガードすら持てるのです!

構文がだいぶ分かってきたと思うので、もう多くの例は必要としません。この節では、並び直していないリストとして表現されたセット(一意な値の集合)のためのappend関数を書きましょう。 これは効率の意味では最悪の実装になていますが、ここで必要なのは構文なのでその例を示します:

prepend(X,[]) ->
    [X];
prepend(X,Set) ->
    case lists:member(X,Set) of
        true  -> Set;
        false -> [X|Set]
    end.

空のセット(リスト)と追加される項Xを渡せば、Xだけを含むリストを返してきます。あるいは関数 lists:member/2 が要素がリストにあるかどうか確認し、もし存在するとなればtrueを返し、そうでなければfalseを返します。 要素Xがすでにセットの中にあれば、そのリストを修正する必要はありません。そうでなければXをリストの最初の要素として追加します。

この例では、パターンマッチはとても単純です。もっと複雑なものもあります。(あなたのコードを 私のコード と比較できます)

beach(Temperature) ->
    case Temperature of
        {celsius, N} when N >= 20, N =< 45 ->
            'favorable';
        {kelvin, N} when N >= 293, N =< 318 ->
            'scientifically favorable';
        {fahrenheit, N} when N >= 68, N =< 113 ->
            'favorable in the US';
        _ ->
            'avoid beach'
    end.

これは、「ビーチに行くべき時間か」を摂氏、絶対温度、華氏の3つの温度表現で答えるという関数です。 すべての状況を満たして答えるように、パターンマッチとガードが一緒に使われています。 前に指摘されたように case ... of 式は多くの関数の先頭で使われているものと同じです。 実際我々のコードを次のように書くことができます:

beachf({celsius, N}) when N >= 20, N =< 45 ->
    'favorable';
...
beachf(_) ->
    'avoid beach'.

ここで疑問が沸き上がってきます:条件式では、いつ if を使って、いつ case ... of を使って、いつ関数を使えばいいのでしょうか?

5.5. どれを使えばいいの?

どれを使うべきかはわりと答えるのが難しい質問です。関数呼び出しと case ... of の違いはとても小さいです。 実際、低レベルの部分では同じように表現されていて、パフォーマンスにおいても甲乙付けがたい状況です。 この2つの1つの違いは、1つ以上の引数が評価される時にあります。 function(A,B) -> ... end. はガードと値のパターンマッチがAとBに対して持てますが、case式ではこのように書かなければなりません:

case {A,B} of
    Pattern Guards -> ...
end.

この形式はあまり見られず、コードを読んでいる人をちょっと驚かせます。似たような状況では、関数呼び出しを使うほうが適切です。 一方で、ちょっと前に書いた prepend/2 関数はほぼ間違い無くcase式を使ったほうが関数呼び出しをして true/false 節を持つようにするよりすっきりします。

他の疑問として、 case や関数のほうがずっと順なんで、ガードを含め if の機能を網羅しているのに、なぜ if を使うのか、というのがあります。 これはすべてのパターンを書かなくてもガードをちょっと追加できるように言語に追加しました。

もちろん、これらの全ては個人的な見解で、あなたのほうがこういった状況に多く遭遇するかもしれません。 きっちりした答えはないのです。このトピックは未だにErlangコミュニティで時々議論されています。 誰も、理解しやすい理由があれば、あなたの選択をコケにしたりはしません。 Ward Cunninghamがかつて言ったように、 「ルーチンを見たときにきれいなコードになっていれば、まさにあなたが期待しているものです」