読者です 読者をやめる 読者になる 読者になる

牌語備忘録 -pygo

あくまでもメモです。なるべくオフィシャルの情報を参照してください。

牌語備忘録 -pygo

すごE本を読んでみたメモ -- 第3章

書籍『すごいErlangゆかいに学ぼう!/Learn You Some Erlang for great good! 』まとめ

第3章 関数の構文

3.1 パターンマッチ

function(Args)
    if X then
〔式〕
else if Y then
〔式〕 else〔式〕

ではなく

function(X) ->
    〔式〕;
function(Y) ->
    〔式〕;
function(_) ->
    〔式〕.

と書く

  • それぞれの function 宣言が 関数節
  • 関数節は ; で区切られ、 関数宣言 でまとめられる。

ちょっと凝ったパターンマッチ

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

head([H|_]) -> H.
second([_,X|_]) -> X.
1> c(functions).
{ok, functions}
2> functions:head([1,2,3,4]).
1
3> functions:second([1,2,3,4]).
2

束縛する変数

  • 束縛されている変数に値を与えようとすると、新しい値が束縛されている値と同じでない限り 、エラーが発生します。
    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").
4> c(functions).
{ok, functions}
5> functions:valid_time({{2013,12,12},{09,04,43}}).
 The Date tuple ({2013,9,6}) says today is: 2013/9/6,
 The time tuple ({9,4,43}) indicates: 9:4:43.
ok
6> functions:valid_time({{2013,09,06},{09,04}}).
 Stop feeding me wrong data!
ok
  • この関数は、タプルが{{A,B,C},{D,E,F}}の形をしている限り、値として文字列でもアトムでもなんでも受け付けてしまう

3.2 ガードだ、ガード!

  • ガードは、パターンマッチの表現力を高めるために 関数のヘッドに書ける追加の節
old_enough(X) when X >= 16 ->
    true;
old_enough(_) ->
    false.
  • ガード式の基本的なルールは、成功時に true を返さなければいけない
  • もし false を返すか、例外を投げるなら、ガードは失敗

複数条件の場合

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.

3.3 If ってなんだ?!

  • if 節はガードのように振る舞い、ガードと同じような構文
  • 関数節の先頭の外側で使う
  • if節はガードパターンと呼ばれている
  • 他の言語で見てきた if とは異なる
-module(what_the_if).
-export([heh_fine/0]).

heh_fine() ->
    if 1 =:= 1 ->
        works
    end,
    if 1 =:= 2; 1 =:= 1 ->
        works
    end,
    if1 =:= 2, 1 =:= 1 ->
        fails
    end.
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
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 X > Y -> a()
 ; true -> b()
end

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

こう置き換える

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

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

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

  • if式がガードのようなものだとしたら、case ... of式は関数のヘッド全体のようなもの
insert(X,[]) ->
    [X];
insert(X,Set) ->
    case lists:member(X,Set) of
        true -> Set;
        false -> [X|Set]
    end.
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.
beachf({celsius, N}) when N >= 20, N =< 45 ->
    'favorable';
...
beachf(_) ->

3.5 どれを使えばいいの?

  • if、case... of、関数という3つのうち、どれを使う かは、わりと答えるのが難しい質問
  • 明らかな違いの 1 つは、1 つ以上の引数 が評価される時
    • function(A,B) -> ...はAとBに対してガードと値のパターンマッチを持てる
    • case 式では
case {A,B} of
    〔パターン〕〔ガード〕-> ...
end.