Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?

プログラミングErlang - 基本2

3章あたり。
Erlangの簡単な逐次的なコードやパターン照合に関する考え方を関数定義で使う方法を学ぶ

fun(無名関数)

  • funの引数は何個でもよい
  • 引数の個数を間違っているとエラーになる
1> Double = fun(X) -> 2 * X end.
#Fun<erl_eval.6.118419387>
2> Double(2).
4
3> TempConvert = fun({c,C}) -> {f, 32 + C*9/5};
3>                  ({f,F}) -> {c, (F-32)*5/9}
3>               end.
#Fun<erl_eval.6.118419387>
4> TempConvert({c,100}).
{f,212.0}
5> TempConvert({f,212}).
{c,100.0}

lists:map(F, L)

  • リストLの各要素を関数Fの引数に適用して得られたリストを返す
1> L = [1,2,3,4].
[1,2,3,4]
2> Double = fun(X) -> 2 * X end.
#Fun<erl_eval.6.118419387>
3> lists:map(Double, L).
[2,4,6,8]

lists:filter(P, L)

  • リストLの要素EのうちP(E)がtrue になるものだけから新しいリストを返す。
1> Even = fun(X) -> (X rem 2) =:= 0 end.
#Fun<erl_eval.6.118419387>
2> lists:filter(Even, [1,2,3,4,5,6,8]).
[2,4,6,8]

for

・lib_misc.erl

for(Max, Max, F) -> [F(Max)];
for(I, Max, F) -> [F(I)|for(I+1, Max, F)].

・Eshell

1> lib_misc:for(1,10,fun(I) -> I end).
[1,2,3,4,5,6,7,8,9,10]

sum

・lib_misc.erl

sum([H|T]) -> H + sum(T);
sum([]) -> 0.

リスト内包表記

1> L = [1, 2, 3, 4, 5].
[1,2,3,4,5]
2> [X * 2 || X <- L].
[2,4,6,8,10]

%% リスト内包表記のジェネレータ部はフィルタと同様に機能する
1> [ X || {a, X} <- [{a,1},{b,2},{c,3},{a,4},hello,"wow"]].
[1,4]

クイックソート

・lib_misc.erl

qsort([]) -> [];
qsort([Pivot|T]) ->
        qsort([X || X <- T, X < Pivot])
        ++ [Pivot] ++
        qsort([X || X <- T, X >= Pivot]).
%% ++ はリストを結合する中置演算子

・Eshell

1> L=[23,6,2,9,27,400,78,45,61,82,14].
[23,6,2,9,27,400,78,45,61,82,14]
2> lib_misc:qsort(L).
[2,6,9,14,23,27,45,61,78,82,400]

ガード

  • パターン照合の威力をさらに高めるための機構
  • ガードを関数定義のヘッドで使う場合はwhenキーワードで始める
  • 式が使えるとこなら、どこでもガードを使うことができる
  • ガードは一連のガード式をカンマ(,)で区切れる
max(X, Y) when X > Y -> X;
max(X, Y) -> Y.
f(X,Y) when is_integer(X), X > Y, Y < 6 -> ...

レコード

定義

  • レコードはタプルの特定要素に名前を付けることができる
  • Nameはレコード名
  • key1,key2などはアトムでなればならない
  • レコードの各フィールドにはデフォルト値を指定できる
  • レコードの定義は2パターンで行える
    • Erlangのソースファイルに定義
      • 複数のモジュール間でrecord定義を利用できない
    • hrl拡張子のファイルに定義
      • 複数のモジュール間でrecord定義を利用できる
      • rr(read records)でレコード定義を読み込む
-record(Name, {
    %% 以下の2 つのキーにはデフォルト値がある
    key1 = Default1,
    key2 = Default2,
    ...
    %% 下記の行はkey3 = undefined と同じ
    key3,
    ...
}).

レコードの作成、更新、取り出し

  • 新しいレコードを作成するには、#todo{key1=Val, ..., keyN=ValN}という構文を使う
  • 作成時、キーをを省略した場合はレコードの定義で指定されているデフォルト値が使われる
  • 作成するレコードは元のレコードのコピーであって元のレコードは変化しない
  • 取り出し方法には2パターンがある
    • パターン照合
    • ドット構文
%% レコード定義をインクルード
1> rr("records.hrl").
[todo]

%% 参照
2> X=#todo{}.
#todo{status = reminder,who = joe,text = undefined}

%% 作成
3> X1 = #todo{status=ugent, text="book"}.
#todo{status = ugent,who = joe,text = "book"}

%% パターン照合で取り出し
4> #todo{who=W, text=Txt} = X1.
#todo{status = ugent,who = joe,text = "book"}
5> W.
joe
6> Txt.
"book"

%% ドット構文で取り出し
7> X1#todo.who.
joe
8> X1#todo.text.
"book"

case式とif

%% case式
case Expression of
    Pattern1 [when Guard1] -> Expr_seq1;
    Pattern2 [when Guard2] -> Expr_seq2;
    ...
    PatternN [when GuardN] -> Expr_seqN
end

%% if
if
    Guard1 ->
    Expr_seq1;
    Guard2 ->
    Expr_seq2;
    ...
    GuardN ->
    Expr_seqN
end

アキュームレータ

  • 1つの関数で2つのリストを効率良く得る

奇数と偶数を分ける ・lib_misc.erl

odds_and_events_acc(L) -> odds_and_events_acc(L, [], []).
odds_and_events_acc([H|T], Odds, Evens) ->
    case (H rem 2) of
        1 -> odds_and_events_acc(T, [H|Odds], Evens);
        0 -> odds_and_events_acc(T, Odds, Evens)
    end;
odds_and_events_acc([], Odds, Evens) -> {Odds, Events}.

・Eshell

1> lib_misc:odds_and_events_acc([1,2,3,4,5,6]).
{[5,3,1],[6,4,2]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment