ラベル Erlang の投稿を表示しています。 すべての投稿を表示
ラベル Erlang の投稿を表示しています。 すべての投稿を表示

2014/07/27

Using SSL for Erlang Distribution

http://www.erlang.org/doc/apps/ssl/ssl_distribution.html に書かれているとおり。

/usr/lib/erlang/releases/17 にあるファイルをネタに次のファイルを作成。

start_ssl.rel

{release, {"Erlang/OTP","17"}, {erts, "6.1"},
[{kernel,"3.0.1"},
{stdlib,"2.1"},
{sasl, "2.4"},
{asn1, "3.0.1"},
{crypto, "3.4"},
{public_key, "0.22"},
{ssl, "5.3.5"}
]}.

boot ファイルを作成

systools:make_script("start_ssl",[]).

/home/ancient/letter/erlang/ssl/erlserver.pem にはサーバ証明書と鍵を書いておく cat pem key > erlserver.pem なイメージ

起動

erl -boot /home/ancient/letter/erlang/ssl/start_ssl -proto_dist inet_tls -ssl_dist_opt server_certfile "/home/ancient/letter/erlang/ssl/erlserver.pem" -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true -sname ssl_test
erl -boot /home/ancient/letter/erlang/ssl/start_ssl -proto_dist inet_tls -ssl_dist_opt server_certfile "/home/ancient/letter/erlang/ssl/erlserver.pem" -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true -sname ssl_test2

(ssl_test2@yarn)8> net_kernel:connect_node(ssl_test@yarn).
true
(ssl_test2@yarn)9> nodes().
[ssl_test@yarn]

ふと気付いた。ssl じゃなくて ssh でやりたかった。

2007/07/27

[Erlang] 待ち行列問題

情報処理試験でよくでてくる待ち行列問題を Erlang でシミュレートしようと思ってやってみました。
でも、ポアソン分布を理解してないためか、思ったような結果になりまんでした。また今度勉強しなおして挑戦してみます。
とりあず、コードは次のとおり。このてのものは Erlang だと書きやすいですね。


-module(wait).
-compile(export_all).

-define(SIZE, 200).

start() ->
case whereis('行列') of
undefined ->
ok;
_ ->
unregister('行列')
end,
register('行列', spawn(?MODULE, '行列', [[]])),
spawn(?MODULE, '窓口', []),
List = lists:map(
fun(_) ->
Sleep = random:uniform(100),
io:format("間隔 ~p~n", [Sleep]),
timer:sleep(Sleep),
spawn(?MODULE, 'お客さん', [self()])
end,
lists:seq(1, ?SIZE)),
Avg = lists:foldl(fun(X, Acc) ->
receive
{Time, X} ->
Time + Acc
end
end,
0,
List) / ?SIZE,
io:format("平均 ~p~n", [Avg]).

'お客さん'(From) ->
Start = now(),
'行列' ! {'並ぶ', self()},
receive
'おつかれさま' ->
ok
end,
Time = timer:now_diff(now(), Start),
io:format("~p~n", [Time]),
From ! {Time, self()}.

'行列'([]) ->
io:format("行列の長さ 0~n"),
receive
{'並ぶ', From} ->
'行列'([From])
end;
'行列'(Queue) ->
io:format("行列の長さ ~p~n", [length(Queue)]),
receive
{'次の方', From} ->
Last = lists:last(Queue),
From ! Last,
'行列'(Queue -- [Last]);
{'並ぶ', From} ->
'行列'([From|Queue])
end.

'窓口'() ->
'行列' ! {'次の方', self()},
receive
From ->
timer:sleep(40),
From ! 'おつかれさま'
end,
'窓口'().

2007/06/19

Erlang サンタクロース問題

サンタクロース問題
43行ではないけど、Erlang で。
なんだか random が怪しい?
after 0 でトナカイを優先しないといけなかったですね。


-module(santa).
-compile(export_all).

-record(stat, {'こびと'=[], 'トナカイ'=[]}).

start() ->
register('サンタ', spawn_link(?MODULE, 'サンタ', [#stat{}])),
lists:foreach(fun(N) -> spawn_link(?MODULE, 'こびと_init', [N]) end,
lists:seq(1, 10)),
lists:foreach(fun(N) -> spawn_link(?MODULE, 'トナカイ_init', [N]) end,
lists:seq(1, 9)).

'サンタ'(#stat{'トナカイ'=X}=Stat) when length(X) == 9 ->
'サンタ働く'("おもちゃくばり", X),
'サンタ寝てる'(Stat#stat{'トナカイ'=[]});
'サンタ'(#stat{'こびと'=X}=Stat) when length(X) == 3 ->
'サンタ働く'("おもちゃ会議", X),
'サンタ寝てる'(Stat#stat{'こびと'=[]});
'サンタ'(Stat) ->
'サンタ寝てる'(Stat).

'サンタ働く'(What, Who) ->
io:format("~s開始...", [What]),
sleep(10),
io:format("終了。~n"),
lists:foreach(fun(A) -> A ! 'おつかれさま。' end, Who).

'サンタ寝てる'(Stat) ->
receive
{'トナカイ', N, X} ->
io:format("トナカイ~bが来た。~n", [N]),
'サンタ'(Stat#stat{'トナカイ'=[X|Stat#stat.'トナカイ']})
after 0 ->
receive
{'こびと', N, X} ->
io:format("こびと~bが来た。~n", [N]),
'サンタ'(Stat#stat{'こびと'=[X|Stat#stat.'こびと']})
end
end.

'こびと_init'(N) ->
random:seed(N, N*N, N*N*N*N),
'こびと'(N).

'こびと'(N) ->
sleep(30),
'サンタ' ! {'こびと', N, self()},
receive
'おつかれさま。' ->
'こびと'(N)
end.

'トナカイ_init'(N) ->
random:seed(N*N*N, N*N*N, N),
'トナカイ'(N).
'トナカイ'(N) ->
sleep(30),
'サンタ' ! {'トナカイ', N, self()},
receive
'おつかれさま。' ->
'トナカイ'(N)
end.

sleep(N) ->
X = random:uniform(N),
timer:sleep(X*1000).

2007/06/18

Erlang オペレータと関数の壁

関数は fun my_module:my_fun/1 のような型で使えるが、オペレータはこれができない。
でも、Erlang ではシングルクウォートで囲めば任意の関数名(atom)を作れるので、次のように '+' とかいう関数を定義してみる。
すると、lists:foldl(fun ope:'+'/2, 0, lists:seq(1, 100)). みたいなことができるけど、嬉しいかなぁ?


-module(ope).
-export(['+'/2, '-'/2, '*'/2, '/'/2, test/0]).

'+'(A, B) ->
A + B.

'-'(A, B) ->
A - B.

'*'(A, B) ->
A * B.

'/'(A, B) ->
A / B.

test() ->
lists:foldl(fun ope:'+'/2, 0, lists:seq(1, 100)).

Erlang error_handler

定義されていない関数が呼ばれたときのハンドラです。
定義されていない関数が呼ばれた場合 error_handler:undefined_function/3 が呼ばれます。
error_handler:undefined_function/3 は呼ばれた関数のモジュールをロードし、ロードできればその関数をもう一度呼んでみる、とうような動きをします。

error_handler:undefined_function/3 をユーザ定義の関数で置き換えることができます。
マニュアルには危険なので要注意、とうようなことが書かれていますが、この機能でモジュールの継承のようなものができないでしょうか。


-module(my_module).
-compile(export_all).

undefined_function(my_module, Func, Args) ->
io:format("no sush funciton!! ~p:~p(~p)~n", [my_module, Func, Args]);
undefined_function(Module, Func, Args) ->
io:format("original handler...~n"),
error_handler:undefined_function(Module, Func, Args).

test() ->
process_flag(error_handler, my_module),
my_module:foo("Hello"),
xxxx:yyyy(zzzz).

2007/06/13

Erlang/OTP R11B-5 has been released

Erlang/OTP R11B-5 がリリースされました。
バイナリ内包表記はまだリリースされないみたいです。

2007/06/11

Erlang 文字列をコンパイルする

文字列をコンパイルする方法です。

erl_scan:tokens で1文ずつスキャン。
erl_parse:parse_form で1文ずつパース。
compile:forms でモジュール単位でコンパイル。
code:load_binary でコンパイルしたものをロードです。

改行コードは \n にしておかないとだめです。


-module(compile_string).
-export([compile/1, test/0]).

compile(FormsStr) ->
Forms = mk_forms(lists:flatten(FormsStr)),
{ok, M, B} = compile:forms(Forms),
{module, M} = code:load_binary(M, atom_to_list(M), B),
M.

mk_forms(String) ->
mk_forms(String, []).

mk_forms([], Acc) ->
lists:reverse(Acc);
mk_forms(S, Acc) ->
{done, {ok, Tokens, _Line}, Rest} = erl_scan:tokens([], S, 0),
{ok, Parsed} = erl_parse:parse_form(Tokens),
mk_forms(Rest, [Parsed|Acc]).

test() ->
S = "-module(fib).
-export([fib/1]).
fib(1) -> 1;
fib(2) -> 1;
fib(N) -> fib(N-1) + fib(N-2).
",
compile(S),
fib:fib(10). % コンパイルした関数を呼出す。

もっと簡単な方法があるような気がします。
Common Lisp ならマクロでおしまいですね。
正規表現を入力にコンパイルされた関数を返す、なんて使い方をしようかと思っています。

2007/05/22

Erlang Emakefile の実行には erl -make

Emakefile の実行は対話環境から make:all(). でもよいけど、コマンドラインの erl -make でもよいです。
Makefile に書くには erl -make がすっきりしてていいですね。escript だなんて戯言を言っていました。


%%-*-erlang-*-
{'*', [debug_info,
{outdir, "../ebin"}]}.

erlc を使ってもいいのですが。

2007/05/20

Erlang ディレクトリ内のファイルを(再帰的に)処理する

filelib:fold_files/5 という関数があります。
1番目の引数がディレクトリ。
2番目が処理対象か否かを判定するための正規表現。
3番目がサブディレクトリも再帰的に処理するか否か。
4番目が各ファイルに適用する関数。
5番目が初期値。
4番目の引数である関数は引数を2つとります。最初の引数は処理対象のファイル。次は最初は5番目に指定した引数で、それ以降はこの関数の返り値です。

サフィックスが erl のファイルをリストにして取得するには次のように書きます。


>1 filelib:fold_files("/home/ancient/letter/erlang", "\\.erl$", true, fun(X, Acc) -> [X|Acc] end, []).
["/home/ancient/letter/erlang/a/fib.erl",
"/home/ancient/letter/erlang/chat/_darcs/pristine/chat.erl",
"/home/ancient/letter/erlang/chat/_darcs/pristine/test_chat.erl",
"/home/ancient/letter/erlang/chat/chat.erl",
[...]|...]

でも、へんにリンクがあるとだめです。

2007/05/19

Erlang クエックブックにインデックスを付ける。プロセスバージョン。

やっぱりプロセスを使わないと。


-module(make_index).
-compile(export_all).

start() ->
lists:foreach(fun(X) -> register(X, spawn_link(?MODULE, holder, [[]])) end,
[head, tail, index]),
register(contents, spawn_link(?MODULE, contents, [head])),
{ok, In} = file:read_file("cookbook_src.html"),
loop(binary_to_list(In)).

holder(V) ->
receive
{get, From} ->
From ! lists:reverse(V);
X ->
holder([X|V])
end.

contents(HorT) ->
receive
tail ->
contents(tail);
'終ったよ' ->
file:write_file("cookbook.html",
[begin
P ! {get, self()},
receive X -> X end
end || P <- [head, index, tail]]);
X ->
HorT ! X,
contents(HorT)
end.

loop([]) ->
contents ! '終ったよ';
loop("****index_place****" ++ T) ->
contents ! tail,
loop(T);
loop("<h2>" ++ T) ->
[Ast|Ref] = erlang:ref_to_list(make_ref()),
index ! io_lib:format("<h3><a href='~s'>", [[Ast|Ref]]),
contents ! io_lib:format("<h2><a name='~s'>", [Ref]),
h2(T);
loop("<h3>" ++ T) ->
[Ast|Ref] = erlang:ref_to_list(make_ref()),
index ! io_lib:format("<a href='~s'>", [[Ast|Ref]]),
contents ! io_lib:format("<h3><a name='~s'>", [Ref]),
h3(T);
loop([X|T]) ->
contents ! X,
loop(T).

h2("</h2>" ++ T) ->
index ! "</a></h3>",
contents ! "</a></h2>",
loop(T);
h2([X|T]) ->
index ! X,
contents ! X,
h2(T).

h3("</h3>" ++ T) ->
index ! "</a><br>",
contents ! "</a></h3>",
loop(T);
h3([X|T]) ->
index ! X,
contents ! X,
h3(T).

Erlang クエックブックにインデックスを付ける

Erlang クエックブック にインデックスを付けました。
手でつけるのはめんどうだったので、Erlang でインデックスを付けるようにしました。
そのソースです。P とか R だったらもっと簡単にできる、というのはここではなしで。
if も case もなしで。
さらに、”クエック”って何よ、というのもなしで。


#!/usr/bin/env escript

main(_) ->
{ok, In} = file:read_file("cookbook_src.html"),
{Index, Contents} = f(binary_to_list(In), [], []),
Dest = insert_index(Index, Contents),
file:write_file("cookbook.html", Dest).

insert_index(Index, Contents) ->
P = find_index(Contents, 0),
{X, Y} = lists:split(P, Contents),
X ++ Index ++ (Y -- "*index_place*").

find_index("*index_place*" ++ _, N) ->
N;
find_index([_|C], N) ->
find_index(C, N+1).

f([], Index, Contents) ->
{lists:reverse(Index), lists:reverse(Contents)};
f("<h2>" ++ T, I, C) ->
[Ast|Ref] = erlang:ref_to_list(make_ref()),
I2 = io_lib:format("<h3><a href='~s'>", [[Ast|Ref]]),
H2 = io_lib:format("<h2><a name='~s'>", [Ref]),
h2(T, [I2|I], [H2|C]);
f("<h3>" ++ T, I, C) ->
[Ast|Ref] = erlang:ref_to_list(make_ref()),
I3 = io_lib:format("<a href='~s'>", [[Ast|Ref]]),
H3 = io_lib:format("<h3><a name='~s'>", [Ref]),
h3(T, [I3|I], [H3|C]);
f([X|T], I, C) ->
f(T, I, [X|C]).

h2("</h2>" ++ T, I, C) ->
f(T, ["</a></h3>"|I], ["</a></h2>"|C]);
h2([X|T], I, C) ->
h2(T, [X|I], [X|C]).

h3("</h3>" ++ T, I, C) ->
f(T, ["</a><br>"|I], ["</a></h3>"|C]);
h3([X|T], I, C) ->
h3(T, [X|I], [X|C]).

2007/05/16

Erlang Windows での ~/.erlang

Windows での ~/.erlang は %HOME% ではなく %HOMEDRIVE%%HOMEPATH% から読みとるらしい。

2007/05/14

perl - telnetコマンドを自作する の Erlang バージョン

perl - telnetコマンドを自作する の Erlang(escript)バージョンです。


#!/usr/bin/env escript

main([Host]) ->
main([Host, telnet]);
main([Host, Port]) when is_list(Port) ->
main([Host,
case catch list_to_integer(Port) of
{'EXIT', _} ->
list_to_atom(Port);
Num ->
Num
end]);
main([Host, Port]) when is_atom(Port) ->
{ok, Num} = inet:getservbyname(Port, tcp),
main([Host, Num]);
main([Host, Port]) ->
{ok, S} = gen_tcp:connect(Host, Port, []),
spawn_link(fun() -> send_loop(S) end),
recv_loop(S).

send_loop(S) ->
Line = io:get_line(''),
gen_tcp:send(S, Line),
send_loop(S).

recv_loop(S) ->
receive
{tcp, _Port, Data} ->
io:format(Data);
{tcp_closed, _Port} ->
exit(normal)
end,
recv_loop(S).

2007/05/13

Erlang standard_io は group_leader()。あるいは、file:read/2 で標準入力からリードする方法。

Erlang の io モジュールで明示的に標準入出力を指定する場合は standard_io を指定するとなっています。
その standard_io って何でしょう。
io モジュールのソースを見ると IoDevice を省略、または stadard_io を指定すると、group_leader() を IoDevice に指定するようになっています。
group_leader() は erlang モジュールで定義されています。
Erlang の全プロセスはいずれかのプロセスグループのメンバーとなっています。
erlang:group_leader/0 はそのプロセスグループのリーダーを返します。
グループ内の IO 処理はそのグループリーダーを通して行われます。
すなわち、Erlang の各プロセスにとって標準入出力とはグループリーダーそのものなのです。
というわけで、標準入出力を使用する時に group_leader() 呼んでいるのです。

以上が、前置きです。
本題は file:read/2 で標準入出力からリードする方法です。
file:read/2 に standard_io を渡しても {error,einval} が返ってきます。
上記をふまえ、ここでは group_leader() の返り値を渡せばいいのです。


1> file:read(standard_io, 1).
{error,einval}
2> file:read(group_leader(), 1).
a
{ok,"a"}


ちなみに、Common Lisp の標準入出力は *standard-input* と *standard-output* ですね。

2007/05/12

Erlang で if って

使わないよね。case は使うけど、if は本当に使いません。
以下、とりとめのない文章ですが。。。
普段の仕事で(Erlang ではない)if → 条件分岐 → テストケース増 という図式があるので、if がないプログラムはとても好ましいのです。
case も条件分岐なので if と変わりないかもしれませんが、case は条件分岐というよりむしろパターンマッチング感が強いので、心理的抵抗が低いのです。
case も関数呼び出しのパターンマッチングに置き換えられるので使わないで済ませられます。
Common Lisp でも if より and や or の方が気持ちがいいです。
単に if 嫌いなのかもしれません。
というわけで、if 嫌いなあなたに Erlang を。

2007/05/09

FizzBuzz

Erlang でやっておこう。


-module(a).
-compile(export_all).

m() ->
F = fun
(_, 0, 0) ->
"FizzBuzz";
(_, 0, _) ->
"Fizz";
(_, _, 0) ->
"Buzz";
(X, _, _) ->
integer_to_list(X)
end,
erlang:display([F(X, X rem 3, X rem 5) || X <- lists:seq(1, 100)]).


Common Lisp でも

(loop for i from 1 to 100
collect (cond ((zerop (rem i 15)) "FizzBuzz")
((zerop (rem i 5)) "Buzz")
((zerop (rem i 3)) "Fizz")
(t i)))

Common Lisp ならマクロで

(defmacro f (sym &rest args)
`(cond
((and ,@(mapcar #'(lambda (a)
`(zerop (rem i ,(car a))))
args)
,(format nil "~{~a~}" (mapcar #'cadr args))))
,@(mapcar #'(lambda (a)
`((zerop (rem i ,(car a))) ,(cadr a)))
args)
(t ,sym)))

(loop for i from 1 to 100
collect (f i (3 "Fizz") (5 "Buzz")))

(loop for i from 1 to 100
collect (f i (3 "Fizz") (5 "Buzz") (6 "Huzz")))


いずれも出力部は手をぬきました。

Erlang リモートプロセスは register できない

Erlang ファイルサーバの置き換えですが、次のようにリレーしなくても登録するだけでいけるのでは、と思って試してみました。
register で badarg が発生して、動きませんでした。
register ではローカルプロセスまたはローカルポートしか登録できないのでリレーが必要のなですね。


-module(pseudo_file_server2).
-export([start/0]).

start() ->
%% ローカルノードのファイルサーバを登録解除
unregister(file_server_2),
%% リモートノードのファイルサーバの pid を取得
Pid = rpc:call('ubu@172.22.10.22', erlang, whereis, [file_server_2]),
%% そのプロセスをローカルノードのファイルサーバとして登録
register(file_server_2, Pid).

2007/05/08

Erlang ファイルサーバプロセスの置き換え


やはり Erlang はすごいです。
古い方の Erlang 本『Concurrent Programing in ERLANG』の 11.9 Relay Techniques に載っていたものですが、心底驚きました。
Erlang ではファイルIOは file_server_2 という名前で登録されたプロセス経由で行います。
その file_server_2 をリモートノードのプロセスで置き換えることにより全てのファイルIOがリモートノードが走っているマシンのファイルシステム対しての操作になります。


-module(pseudo_file_server).
-export([start/0, relay/1]).

start() ->
%% ローカルノードのファイルサーバを登録解除
unregister(file_server_2),
%% リモートノードへのリレーループプロセスを開始
Pid = spawn(?MODULE, relay, ['ubu@172.22.10.22']),
%% そのプロセスをローカルノードのファイルサーバとして登録
register(file_server_2, Pid).

relay(Node) ->
%% リモートノードのファイルサーバの pid を取得
Pid = rpc:call(Node, erlang, whereis, [file_server_2]),
%% リレーループ開始
loop(Pid).

loop(Pid) ->
%% ファイルサーバとして受信したものをリモートノードへリレー
receive
Any ->
Pid ! Any
end,
loop(Pid).

次は実行例です。

2> pseudo_file_server:start().
ture
3> file:write_file("/tmp/a.txt", "hello").
ok

/tmp/a.txt はローカルのファイルシステムではなく、リモートマシンファイルシステム上さ作成されます。
なんと素晴しい。
本の中ではファイルシステムを持たない組み込みシステム等でも、このテクニックによりファイルシステムを使える、とういような説明でした。
『Concurrent Programing in ERLANG』は第一版が1993年、第二版が1996年に出版されています。これまでの間、私たちはいったい何をしていたのでしょうか。
単にマルチコアの普及により Erlang のアーキテクチャが生きるようになっただけなのでしょうが。
素敵ですね。

Yaws 組み込みモードの設定

Yaws 組み込みモードで他のマシンからアクセスできるようにするには、listen と servername の指定も必要です。


SC = [{port, 8880}, % ポート番号
{servername, "www.example.com"}, % サーバ名
{listen, {0, 0, 0, 0}}], % リッスンアドレス
GC = [{logdir, "/tmp"}], % グローバル設定
yaws:start_embedded("/var/www", SC, GC). % 組み込みモードでスタート

2007/05/07

Erlang io の引数に lists:flatten は必要ない

io_lib:format/2 の返り値が、単なる文字列ではなく、文字と文字列のリストになるのは不便なのに何故だろう、と思っていました。
ふとしたことで文字と文字列のリストそのままで出力できることに気づきました。
lists:flatten はいらないのですね。
file:write_file/2 でもネストしたリストそのままで大丈夫でした。


49> io_lib:format("Hello ~s!~n", ["World"]).
[72,101,108,108,111,32,"World",33,"\n"]
50> io:format([72,101,108,108,111,32,"World",33,"\n"]).
Hello World!
ok

そういえば、次のようなのもOKなんですよね。

51> "Hello" " " "World" "!\n".
"Hello World!\n"