2007/05/01

erlang その2

今度は、標準モジュールの説明。
マニュアルはコマンドラインから
# erl -man io
で見ることができる。この場合は、ioモジュールのマニュアルが表示される。
これはerlangのシェルからではなく、csh等の通常のシェルから実行する。

# io:format("test~n").
test
ok
# io:format("test~n", []).
test
ok

引数なしでも空リスト引数でも表示できる。

# io:format("test ~w.~n", [AAA]).
** 1: variable 'AAA' is unbound **
大文字だと文法違反のようだ。

# io:format("test ~w.~n", [aaa]).
test aaa.
ok
小文字ならOK。

# io:format("test ~w.~n", ["AAA"]).
test [65,65,65].
ok
""で囲むと文法違反にはならないが、文字列ではなく数字のリストに変換される。

6> io:format("test ~w.~n", ['AAA']).
test 'AAA'.
ok
''で囲むと望みどおりのAAAが表示される。

# io:format("test 1:~w 2:~w ~~.~n", ['AAA','BBB']).
test 1:'AAA' 2:'BBB' ~.
ok
2つ以上表示させたい場合は、リストの要素を増やせば良い。
フォーマット部分の文字列には~wを2個書く。このへんはC++のboost:formatのようだ。
~を表示させたいときは~~と書けば良い。

次に最小値を求める関数を作成する。
ここでは「guard」というのを使用する。

<tut6.erl>
-module(tut6).
-export([list_min/1]).
list_min([Head|Rest])->list_min(Rest, Head).
list_min([], Result)->Result;
list_min([Head|Rest], Result) when Head < Result ->list_min(Rest, Head);
list_min([Head|Rest], Result)->list_min(Rest, Result).

実行すると、
# c(tut6).
./tut6.erl:6: Warning: variable 'Head' is unused
{ok,tut6}
# tut6:list_min([7,2,5,7,9,1,3]).
1
のようになり、最小値が得られる。「when」というのが関数実行の条件で、
trueの場合に関数の中身が実行される。そうでない場合は、関数の次の定義が実行される。
このように条件をテストすることを「guard」と呼ぶようだ。

guardで使える条件文は <, >, == <=, >=, /=がある。
/=はnot equalであり、C++とは違うので注意。

リストにはもう少し機能がある。| operatorにはリストの先頭に要素を追加する機能がある。
まず、リストを作成する。
# L = ['AAA','BBB'].
['AAA','BBB']

| operatorでリストの先頭に要素を追加する。
# L3 = ['CCC'|L].
['CCC','AAA','BBB']

逆向きに書くと、
# L2 = [L|'CCC'].
[['AAA','BBB']|'CCC']
となり、'CCC'のみ持つリストの先頭に['AAA','BBB']というリストが
追加されることになる。

これを使って、リストの順序を逆にする関数を作ってみる。
<tut8.erl>
-module(tut8).
-export([reverse/1]).
reverse(List)->reverse(List, []).
reverse([Head|Rest], ReversedList)->reverse(Rest, [Head|ReversedList]);
reverse([], ReversedList)->ReversedList.

4行目でHeadから順にReversedListの先頭に追加しているところがミソ。
# c(tut8).   
{ok,tut8}
# tut8:reverse([1,2,3,4,5]).
[5,4,3,2,1]
逆順になった。

関数に連続した処理をさせたい場合は、「,」で区切ればよく、
<tut7a.erl>
-module(tut7a).
-export([format/1]).
format(Str)->
  io:format("test : ~w~n", [Str]),
  io:format("test2 : ~w~n", [Str]).

のように書ける。これを実行すると、
# c(tut7a).    
{ok,tut7a}
# tut7a:format(aa).
test : aa
test2 : aa
ok

となり、文字列の出力を2回行うようになる。
今度は与えられた値のリストから最小値と最大値をとってみる。
<tut7b.erl>
-module(tut7b).
-export([minmax/1]).

minmax([Head|Rest])->minmax(Rest, Head, Head).
minmax([Head|Rest], Min, Max)->
  if Head < Min -> NewMin = Head;
    true -> NewMin = Min
  end,
  if Head > Max -> NewMax = Head;
    true -> NewMax = Max
  end,
  minmax(Rest, NewMin, NewMax);
minmax([], Min, Max) -> {Min, Max}.

コンパイルして実行してみる。
# c(tut7b).
{ok,tut7b}
# tut7b:minmax([1,2,3,4,5,6,7]).
{1,7}
# tut7b:minmax([5,3,8,0,2,4,5]).
{0,8}

たしかに最小値と最大値がとれている。
条件文ifは上記のように書けばよく、条件の間は;で区切る。
endの直前の条件文には;はいらない。
elseがないので、代わりに一番最後にtrue->とすることで対応する。

もう少しifについて調べてみる。
<tut9.erl>
-module(tut9).
-export([test/2]).

test(X,Y)->
  if X==1 -> 'X == 1';
    Y==2 -> 'Y == 2';
    X==2, Y==5 -> 'X==2, Y==5';
    X==3;
    Y==4 -> 'X==3 or Y==4';
    (X==5) and (Y==5) -> 'X==5 and Y==5'
  end.

実行すると、
# c(tut9).
{ok,tut9}
# tut9:test(1,2).
'X == 1'
Xが1なので、一番最初の条件に一致する。

# tut9:test(0,2).
'Y == 2'
Xが1でなく、Yが2なので、2番目の条件に一致する。

# tut9:test(2,5).
'X==2, Y==5'
今度は3番目の条件に一致する。

# tut9:test(3,5).
'X==3 or Y==4'
これは4番目の条件に一致する。
C++でいうswitch-cast文と似ていて、;のみで->がない場合は、
->が見つかったところから実行するようだ。

# tut9:test(5,5).
'X==5 and Y==5'
最後は両方共が5の場合の条件である。

# tut9:test(6,5).

=ERROR REPORT==== 1-May-2007::17:01:33 ===
Error in process <0.44.0> with exit value: {if_clause,[{tut9,test,2},{shell,exprs,6},{shell,eval_loop,3}]}

** exited: {if_clause,[{tut9,test,2},{shell,exprs,6},{shell,eval_loop,3}]} **
条件にマッチしないとこんな風にエラーになる。

ちなみに10行目を
 (X==5) and (Y==5)
と書かず、
 X==5 and Y==5
と書くとコンパイルエラーになる。

最後に高階関数(higher order functions)について試してみる。
C++でいう関数ポインタみたいなもの。
シェルから使うと、次のようになる。

18> Fn = fun (X)->X*3 end.
#Fun<erl_eval.6.56006484>
19> Fn(5).
15

引数の値を3倍する関数Fnを定義して、その引数に5を入れると15が出力される。
高階関数の定義はfunとendで囲むようだ。

標準モジュールのlistsを使うと、次のようなことができる。
20> lists:map(Fn, [1,2,3]).
[3,6,9]

リスト[1,2,3]の各要素に対してFnを適用し、その結果をリストに入れる操作が行われている。

他にも、
25> Fp = fun (X)->io:format("elm : ~w~n",[X]) end.
#Fun<erl_eval.6.56006484>
26> lists:map(Fp, [1,2,3]).
elm : 1
elm : 2
elm : 3
[ok,ok,ok]
27> lists:foreach(Fp, [1,2,3]).
elm : 1
elm : 2
elm : 3
ok
といったことができる。

ソートも次のようにできる。
30> lists:sort([2,3,1]).
[1,2,3]
31> lists:sort(fun(X,Y)->X<Y end,[2,3,1]).
[1,2,3]
32> lists:sort(fun(X,Y)->X>Y end,[2,3,1]).
[3,2,1]

名前とペアになっている要素からなるリストに対してもソートできる。
33> lists:sort(fun({_,X},{_,Y})->X<Y end,[{'A',2},{'B',3},{'C',1}]).
[{'C',1},{'A',2},{'B',3}]

ここで、「 _ 」はanonymous variableと呼ばれ、使わない値に使用するようだ。
今の例では、文字は比較に使わないので「 _ 」を使って表現を簡略化した。

今回はここまで。

0 件のコメント :