Displaying posts filed under

Erlang

Erlangについての投稿

選択受信を理解する

『プログラミングErlang』8.6で解説されている選択受信について、ちゃんと理解できているか自信がなくなってきたので、下のようなコードを書いて実験した。

loop(X) ->
    Y = X + 1,
    receive
        X ->
            io:format("received: ~p, continue.~n", [X]),
            loop(Y);
        Y ->
            io:format("received: ~p, finish.~n", [Y])
    end.

receiveのパターンにマッチしなかったメッセージは捨てられるわけではない。プロセスの「メールボックス」に保存されたまま照合の機会を待ち続ける。

9> Pid = spawn(fun() -> lib_misc:loop(1) end).
<0.49.0>
10> Pid ! 1.
received: 1, continue.
1
11> Pid ! 2.
received: 2, continue.
2
12> Pid ! 6.
6
% この時点ではマッチしない

13> Pid ! 3.
received: 3, continue.
3
14> Pid ! 4.
received: 4, continue.
4
received: 6, finish.
% 再び照合が行われ、マッチした!

Erlangのインストールと再インストール

Ubuntu 8.10にR12B-5をインストール

分散Erlangの実験を行うために、VMWare上のUbuntuに最新版のErlang(R12B-5)をインストールした。aptではR12B-3までしか提供されていなかったので、公式サイトからソースを落としきてコンパイル。Ubuntuインストール直後の状態では以下3つのパッケージが足りていなかった。

  • libncurses5-dev
  • m4
  • sun-java6-jdk

READMEにはJDKはオプションだと書いてあるのに、入れないとコンパイルが完了しなかった。無効にするconfigureオプションも見当たらない。

手順については特筆するようなことはなし。

tar xvzf otp_src_R12B-5.tar.gz
cd  otp_src_R12B-5
./configure && make
sudo make install

Mac OS X 10.5 にSMPオプション付きでインストール

OS XではMacPortsを使って一発で最新版がインストールできる。素晴らしい。が、デフォルトではSMPオプションが有効になっていないので、smp variants を指定して入れ直すことにした。SMPオプションがどういうものなのか、まだよく分かってないけど。

sudo port deactivate erlang
sudo port install erlang +smp

GeSHiのErlangプラグインでセミコロンを正しく扱う

Kreisquadratur» Blog Archive » Erlang syntax highlighting for GeSHi

こちらで配布されているErlangプラグインを利用するとセミコロンが <SEMI> に変換されてしまう、という問題について。先日は変数名のハイライトを諦めることでお茶を濁したが、やっぱりこれはGeSHiの仕様が悪いだろう、と思ったのでgeshi.phpを次のように修正した。

(’<SEMI>’ を “\x8″ に全置換しただけ。下のパッチはバージョン1.0.8の場合)

@@ -1942,7 +1942,7 @@
 
             //This fix is related to SF#1923020, but has to be applied regardless of
             //actually highlighting symbols.
-            $result = str_replace(array('<SEMI>', '<PIPE>'), array(';', '|'), $result);
+            $result = str_replace(array("\x8", '<PIPE>'), array(';', '|'), $result);
 
             // Timing is irrelevant
             $this->set_time($start_time, $start_time);
@@ -2774,7 +2774,7 @@
         //This fix is related to SF#1923020, but has to be applied regardless of
         //actually highlighting symbols.
         /** NOTE: memorypeak #3 */
-        $endresult = str_replace(array('<SEMI>', '<PIPE>'), array(';', '|'), $endresult);
+        $endresult = str_replace(array("\x8", '<PIPE>'), array(';', '|'), $endresult);
 
 //        // Parse the last stuff (redundant?)
 //        $result .= $this->parse_non_string_part($stuff_to_parse);
@@ -3952,7 +3952,7 @@
             //Circumvent a bug with symbol highlighting
             //This is required as ; would produce undesirable side-effects if it
             //was not to be processed as an entity.
-            ';' => '<SEMI>', // Force ; to be processed as entity
+            ';' => "\x8", // Force ; to be processed as entity
             '|' => '<PIPE>' // Force | to be processed as entity
             );                      // ENT_COMPAT set

コードの中にバイナリの 0×08 = 制御文字のバックスペースが含まれることはまずないので、問題にはならないはず。少なくとも <SEMI>よりはマシなはず。

これでErlangモードがきちんと動くようになった。

fac(0) -> 1;
fac(N) -> N * fac(N-1).

死んだはずのプロセスからメッセージが届く

『プログラミングErlang』8章に載っている on_exit/2 を使うと、既に死んでいるプロセスから死亡通知メッセージが届く、ように見える。

1> Foo = fun() -> 1 end.
#Fun<erl_eval.20.67289768>
2> Pid = spawn(Foo).
<0.33.0>
3> erlang:is_process_alive(Pid).
false
4> lib_misc:on_exit(Pid, fun(Why) -> io:format("~p dead with:~p~n", [Pid, Why]) end).
<0.33.0> dead with:noproc
<0.36.0>

これは erlang:link/1 の性質によるもの。man erlang より引用。

* If the calling process is not trapping exits, and checking Pid is cheap — that is, if Pid is local — link/1
fails with reason noproc.

* Otherwise, if the calling process is trapping exits, and/or Pid is remote, link/1 returns true, but an exit sig-
nal with reason noproc is sent to the calling process.

link/1の呼び出し自体は成功する、すなわち正常にリンクが確立できたように見える、というところがポイント。

この性質のせいで 9.8 の「キープアライブプロセス」の説明がおかしなことになっている。理解するのに丸一日かかったよ……。

『プログラミングErlang』8章 練習問題(2)

昨日貼り付けたコードにはバグがあったので、公開後に一度修正した(loop/3)。その時にリングを構築するための別の方法も思いついたので、下に示しておく。一度にまとめてプロセスを作るのではなく、子が孫を生む方式。

construct(N) when N > 0 ->
    spawn(?MODULE, prepare, [N]).
 
prepare(1) ->
    lib_ring:entryloop(self());
prepare(N) ->
    Next = spawn(?MODULE, prepare, [self(), self(), N-1]),
    lib_ring:entryloop(Next).
 
prepare(First, Prev, 1) ->
    lib_ring:loop(Prev, First);
prepare(First, Prev, N) ->
    Next = spawn(?MODULE, prepare, [First, self(), N-1]),
    lib_ring:loop(Prev, Next).

これで実行時間はさらに短縮された。プロセス数が多くなるほど差が顕著になる。

辞書方式:

N=100000, M=100
time=8.65, (9.113) seconds
N=200000, M=10
time=6.41, (7.012) seconds

子孫方式:

N=100000, M=100
time=7.05, (7.573) seconds
N=200000, M=10
time=1.67, (2.005) seconds

『プログラミングErlang』8章 練習問題

8.11の練習問題。これ本の中には答え載ってないのかな…?

自分なりの回答。時間計測部分のコードは省略。

  • コードの簡潔さを優先してリングの”始点”は固定にした
  • リストを配列代わりに使っているので効率が悪い

(続きを読む…)

Erlangを書くための準備

今さらながらErlangのための環境整備。

Emacsのerlang-mode追加
Emacs用のerlang-modeはErlangの配布物に含まれている。Mac Portsでインストールした場合は
port contents erlang | grep emacs
でファイルが見つかる。

GeSHiにErlangプラグイン追加
wp-syntaxでハイライト表示するために、GeSHi用のErlangプラグインを追加。下記のURLで公開されているものを利用させてもらった。

Kreisquadratur» Blog Archive » Erlang syntax highlighting for GeSHi

しかしそのままだとセミコロンが<SEMI>に変換されてしまうというバグがあるため、変数名のハイライトを諦めて下記のように修正した。

@@ -113,7 +113,7 @@
         ),
     'REGEXPS' =&gt; array(
         // Variable
-        0 =&gt; '[A-Z][_a-zA-Z0-9]*',
+        //0 =&gt; '[A-Z][_a-zA-Z0-9]*',
         // File Descriptor
         4 =&gt; '&lt;[a-zA-Z_][a-zA-Z0-9_]*&gt;'
         ),

これはどちらかといえばGeSHiの仕様上の制約らしい。うまい解決策が見つかればフィードバックしたいのだが…。

『プログラミングErlang』6章まで読んだ

やっと6章まで読み終わった。いよいよ7章から並行処理に入る。

ところで6章には「コマンドライン引数を使うプログラム」の例として階乗を計算するプログラムが載っているのだが、これをそのまま実行すると引数の数が合わない場合にクラッシュダンプを吐く。流石にもうちょっと何とかならないかと思って調べてみたところ、次のようなことが分かった:

  • コマンドライン引数の数によって呼び出される関数が変わる。erl -s module func と指定した場合、コマンドライン引数を一つも与えなかった場合は module:func/0 が、一つ以上与えた場合は module:func/1 が呼び出される。
  • module:func/1の引数はアトムのリストである。
  • escriptではこの差異を吸収して、アトムをリスト(文字列)に変換した上で main/1 を呼び出している。

ということで escript の動作にあわせて次のようにしてみた。

-module(fac2).
-export([run_main/0, run_main/1]).
 
run_main()  -> main([]).
run_main(L) -> main(lists:map(fun atom_to_list/1, L)).
 
main([A]) ->
    I = list_to_integer(A),
    F = fac(I),
    io:format("factorial ~w = ~w~n", [I, F]),
    init:stop();
main(_) ->
    io:format("Usage: ~p:rum_main <num>~n", [?MODULE]),
    init:stop().
 
fac(0) -> 1;
fac(N) -> N * fac(N-1).

起動は erl -noshell -s fac2 run_main とする。


それと、6章にはMakefieのテンプレートも載っているのだが、その中の .SUFFIXES および .erl.beam といったターゲット名は古い形式の定義らしい。新しい形式では型ルールを使って次のように書けば良いようだ。

%.beam: %.erl
erlc -W $<