はとのーと

エジソンノート(アイデア、思い付き、メモ)として使っています。誰かの役に立つかもしれないので公開しています。

Erlangについてのメモ

(2024-04-06更新)

Erlangについての自分用のメモです。

目次:

参考ページ

プロセス

プロセスの基本

プロセスはspawnで作成する。

spawn(モジュール, 関数, 引数)で指定したモジュールの指定した関数を新しいプロセスで実行する。 戻り値はPid。 spawn(?MODULE, start, [])のように指定する。

spawn(fun() -> ... end)のように無名関数を作成して実行することもできる。

リンク

メインプロセスから spawn したプロセスは link することで同時に終了させることができる。

Pid = spawn(spawn_test, say_something, [hello, 3]),
link(Pid).

spawn_link/3を使うとspawnlinkを一度に行なうことができる。

unlink/1でリンクを解除する。リンクはスタックできないため、何度linkしても1回のunlinkで解除できる。

リンクしたプロセスが終了する時は終了シグナルを送ってくる。 終了シグナルは双方向で、どちらのプロセスが終了しても相手側に送られる。

process_flag(trap_exit, true)をするとプロセスが終了シグナルで連鎖終了しなくなり、受け取った終了シグナルをメッセージに変換することができる。

受け取るメッセージ:

  • {'EXIT', Pid, normal} - 通常終了、exit(normal)で終了した場合
  • {'EXIT', Pid, Reason} - exit(Reason)で終了した場合 (Reasonは文字列でもよい)
  • {'EXIT', Pid, killed} - exit(Pid, kill)で終了した場合

自分で exit(self(), kill)した場合にはメッセージは送られずプロセスが終了し、連鎖的にリンクされたプロセスも終了する。

モニター

モニターは以下の特徴を持つ特殊なリンクと考えることができる。

  • 一方向である
  • スタックできる

リンクと違って連鎖的にプロセスが終了することはない。 そのため、他プロセスの監視に使うことができる。

Pid = spawn(spawn_test, say_something, [hello, 3]),
Ref = monitor(process, Pid).

spawn_monitor/3を使うとspawnmonitorを一度に行なうことができる。

プロセスが終了した時には{'DOWN', Ref, process, Pid, Reason}のようなメッセージが送られてくる。

demonitor(Ref)でリンクを解除する。 demonitor(Ref, [flush, info])のようにオプションを付けることができ、flushはメールボックスDOWNメッセージを削除し、infoはモニターが存在したかどうかを真偽値で返す。

プロセスに名前をつける

register/2 でプロセスに名前をつける。 名前はアトムで指定する。

Pid = spawn(spawn_test, say_something, ["hello", 3]),
register(hello, Pid).

名前のついたプロセスは whereis/1 で pid を得ることができる。

Pid = whereis(hello).

また、名前そのものをメッセージの送信先として使うことができる。

hello ! {self(), "world"}.

unregister/1 で名前を削除できる。

unregister(hello).

登録された名前は registered/0 で取得することができる。

1> registered().
[logger_sup,rex,user,erts_code_purger,kernel_safe_sup,init,
 erl_signal_server,logger_proxy,global_name_server,
 kernel_sup,inet_db,standard_error_sup,standard_error,
 logger_handler_watcher,file_server_2,kernel_refc,
 erl_prim_loader,global_group_check,logger_std_h_default,
 logger,code_server,global_group,socket_registry,
 application_controller]

dict:append/3dict:store/3 の違い

dict:append/3 を使うと値が配列で保存される。 これは同じキーの複数の値を格納することを想定しているからである。

1> D = dict:append(key, value, dict:new()).
{dict,1,16,16,8,80,48,
      {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
      {{[],[],[],[],[],[],[],[],[],
        [[key,value]],
        [],[],[],[],[],[]}}}
2> dict:find(key, D).
{ok,[value]}

dict:store/3 を使うと値がそのまま保存される。

1> D = dict:store(key, value, dict:new()). 
{dict,1,16,16,8,80,48,
      {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
      {{[],[],[],[],[],[],[],[],[],
        [[key|value]],
        [],[],[],[],[],[]}}}
2> dict:find(key, D).                     
{ok,value}

数値を2進数8桁右寄せで足りない桁を0で表示する

画面に表示するには io:format/2io:fwrite/2 を使う。 文字列に変換するには io_lib:format/2io_lib:fwrite/2 を使う。

以下ではわかりやすくするため io_lib:format/2 を使い段階的に書式を追加する。

% 16進数の18を2進数で表示する
1> io_lib:format("~.2B", [16#18]).
"11000"

% 16進数の18を2進数桁8桁右寄せで足りない桁は空白で表示する
2> io_lib:format("~8.2B", [16#18]).
"   11000"

% 16進数の18を2進数桁8桁右寄せで足りない桁を#で埋めて表示する
3> io_lib:format("~8.2.#B", [16#18]).
"###11000"

% 16進数の18を2進数桁8桁右寄せで足りない桁を0で埋めて表示する
4> io_lib:format("~8.2.0B", [16#18]).
"00011000"

数値を書式指定で文字列に変換するには io_lib:format の結果に lists:flatten を使う

io_lib:format は内容によって結果がネストしたリストで返ってくるため文字列にするには lists:flatten を使う。

1> io_lib:format("~p~c",[65,65]).   
["65",65]
2> lists:flatten(io_lib:format("~p~c",[65,65])).
"65A"

BIF のソースは bif.c

BIF (built-in functions) のソースは GitHub の bif.c です。

wxErlang

wxWidgets 自体の情報も載せる。

参考資料

get_env(), set_env()

Erlangでは他のプロセスの変数にアクセスできないため、wxErlangの描画イベント時にエラーが発生することがある。 この場合にはメインプロセスから get_env() で取得したものを set_env(Env) でセットすればよい。

wxBitmap と wxImage の違い

  • wxBitmap - プラットフォーム依存で DC への描画が簡単・高速
  • wxImage - プラットフォーム中立で RGB + アルファ (透明度) のついたバッファでサイズやスケール変更、クリッピングなどができる

wxBitmap と wxImage は相互に変換が可能。

wxBitmap wxImage
特徴 プラットフォーム依存 プラットフォーム独立
それ自体への描画 wxMemoryDC 経由で自由に描画 setRGB でピクセル、四角形を描く。getData/setData でデータ列を直接操作可能
画面への描画 wxDC:drawBitmap, wxDC:blit 不可

参照: wxWidgets: wxBitmap Class Reference

wxPen と wxBrush

  • wxPen - 図形の輪郭スタイルを表す
  • wxBrush - 図形の塗り潰しスタイルを表す

wxPen を透明にするには style に ?wxTRANSPARENT を設定する。

% 輪郭を透明にする (描かない)
wxDC:setPen(DC, wxPen:new(?wxBLACK, [{style, ?wxTRANSPARENT}])).

% 白で塗り潰す
wxDC:setBrush(DC, wxBrush:new(?wxWHITE)).

Emacs erlang-mode

Erlang -- Erlang mode for Emacs - 公式ドキュメント

~/.emacsか~/.emacs.d/init.elに以下のように記入する。 (パスはArch Linuxの例)

(setq load-path (cons "/usr/lib/erlang/lib/tools-3.2.1/emacs/" load-path))
(setq erlang-root-dir "/usr/lib/erlang/lib/")
(setq exec-path (cons "/usr/lib/erlang/bin/" exec-path))
(require 'erlang-start)

erlang.elでインデントにスペースを使うようにする - YAMAGUCHI::weblog

インデントをタブではなくスペースにするには以下を追加する。

(add-hook 'erlang-mode-hook '(lambda() (setq indent-tabs-mode nil)))

代表的な操作:

  1. コードは自動的にインデントされる。インデントが間違っていたらTABで調整
  2. 選択範囲を C-c C-cコメントアウトC-c C-u でコメントを外す
    • 現在行ではなく選択範囲に対して行われるので注意すること
  3. コードを書いたら C-c C-kコンパイル
  4. Erlang shellバッファが開くので操作する
  5. Erlang shellバッファを閉じるにはinit:stop().を入力してからC-x kでバッファを削除

Emacsについては GNU Emacsについてのメモ - はとのーと にまとめました。