Hatena::Grouperlang

檜山正幸のErlang未確認情報 RSSフィード

2009-04-22 (水)

やっとわかった桁指定パディング付きフォーマット

| 09:07

printfの"%2d"みたいのが今までできなかった(苦笑)。

100> io:fwrite("~5.8.0B~n", [31]). 
00037
ok
101> io:fwrite("~6.16.0B~n", [31]). 
00001F
ok
102> io:fwrite("~6.10._B~n", [31]). 
____31
ok
103> 

ま、調べりゃすぐ分かることだけど。

  • 「~」 桁数 「.」 基数 「.」 パディング文字 「B」
  • 「~」 桁数 「.」 基数 「B」 (デフォルトは空白)
  • 「~」 桁数 「B」 (デフォルトは基数10;十進数
  • 「~」 「B」 (デフォルトはキッチリ詰める)

なんでBなのか? わからん、謎じゃ。

[追記]パディング文字は~も含めて何でもいいようだが、*はダメ。*は、なんか特殊みたい。[/追記]

2009-04-08 (水)

乱数からIDを作る

| 13:42

YAWSソースから抜き出したサンプル。

%% -*- coding: utf-8 -*-

%% @doc 乱数に基づく「ほぼID」の生成
-module(randomid).
-export([init/0, gen/0]).

%% spec () -> {integer(), integer(), integer()}
seed() ->
  now(). % {X, Y, Z}

%% @spec () -> ok
init() ->
  {X,Y,Z} = seed(),
  random:seed(X, Y, Z),
  ok.

%% @spec () -> string()
gen() ->
  N = random:uniform(16#ffffffffffffffff), %% 64 bits
  integer_to_list(N).

2009-04-03 (金)

ヘッダファイルのオマジナイ

| 13:08

あー、C言語のヘッダでやってたよなー。なつかし。

-ifdef(FOO_HRL).
-define(FOO_HRL, true)
 %% ...
 %% ...
-endif. % FOO_HRL

2009-04-02 (木)

void, nullがない。

| 16:34

voidとかnullのように、標準的などうでもいい型、無効値がない。

アームストロングvoidというアトムが好きらしい。okを返している例も多い。undefinedもよくあるな。僕はnoneが好きでよく使っている(標準ライブラリでもnoneの例がある)。

最近思ったのだが、noneやundefinedは、積極的に「値がない/未定義」を意味するから、ほんとにどうでもいい、値を使うのはやめてくれ、捨ててくれ、ってときはvoidのほうがいいかも知れない。捨てられるのだから何だっていいんだけど、コンベンションとしてね。

size/1, tuple_size/1

| 17:31

size/1以外にtuple_size/1てのもある。もちろん、tuple専用。なぜか、binary_size/1はない。sizeが型オーバーロードしているなら、listのlengthも分かればいいのに。

wbdjkcqcpswbdjkcqcps2013/08/28 02:13vvoskfsmboh, <a href="http://www.teetlxcydi.com/">zeptfodakf</a> , [url=http://www.lfprwfvnog.com/]hrjlynvtod[/url], http://www.gjjqilpgam.com/ zeptfodakf

thxpfkitzuthxpfkitzu2014/03/18 18:03mxfmdfsmboh, <a href="http://www.cgeuxhshnw.com/">jxarsqwhxg</a> , [url=http://www.pfyavtpqjp.com/]pficlukqwf[/url], http://www.alpsvdvtvq.com/ jxarsqwhxg

lelocffzgtlelocffzgt2014/04/12 15:46vsmpafsmboh, <a href="http://www.mvqsrcvjyt.com/">jwqzrmhbxn</a> , [url=http://www.wnopwsfrwr.com/]xmzsismghz[/url], http://www.zpckatvivp.com/ jwqzrmhbxn

2009-04-01 (水)

LETマクロとIFマクロ

| 10:52

Eunitのヘッダから:

-define(LET(X,Y,Z), ((fun(X)->(Z)end)(Y))).

-define(IF(B,T,F), (case (B) of true->(T); false->(F) end)).

2009-03-31 (火)

ミニシェル

| 10:46

テケトウに作ったばっかりのものだけど、けっこう便利なんじゃねーの、コレ。改善の余地はある(つうか、作り直すだろう)けど、とりあえず使える。

このエントリーマニュアル風に書く。最後にソースコードが貼ってあるので、これを見るのが一番速いかも。「apply、アブネーー」の問題は放置してある、要注意。

それと、Edocで生成したHTMLドキュメント:

動機とウリ

ErlangにはEシェルがあるので、対話的な実験やテストができて便利。だが、同じような、しかし少し違ったコマンドを繰り返し繰り返しタイプするのにはウンザリ。コマンド入力をもっと簡略化できないの? というのが動機。

ミニシェル(minshモジュール*1)は、汎用のコマンドライン・フロントエンドで、コマンド関数を実装したコールバックモジュールにより、さまざまな用途に使える。

例えば、よく使うコマンド(関数呼び出し)を1文字、あるいは[ENTER]のみにすることができる。簡易なgen_serverのような機能を持っているので、関数ライブラリのgen_server化の前に、手入力でのテストができる。また、既にあるgen_serverへのフロントエンドとしても使える。

使い方

コマンド関数を実装したコールバックモジュール名を指定して起動する。開始のバナー(↓)はどうせ変わるるだろう。

15> minsh:start(foo).

*** Starting shell (command module: foo) ***

This is foo.

(foo)$  

ミニシェルからコールバックモジュールにどんな呼び出しがかかるかは、次のモジュールをコールバックに指定していじれば分かる。

%% -*- coding: utf-8 -*-

-module(command_echo).
-export([command_init/1, command_fin/1, command/3]).

command_init(Arg) ->
  io:format("~nThis is " ++ atom_to_list(?MODULE) ++ ".~n"),
  io:format("cammand_init/1 is calld: Arg = ~p~n~n", [Arg]),
  0.
  
command_fin(Arg) ->
  io:format("~nThis is " ++ atom_to_list(?MODULE) ++ ".~n"),
  io:format("cammand_fin/1 is calld: Arg = ~p~n", [Arg]),
  ok.
command(Cmd, Arg, State) ->
  io:format("cammand/3 is calld: Cmd = '~s', Arg = ~p, State=~p~n", 
            [Cmd, Arg, State]),
  {ok, State + 1}.

コールバックモジュールの書き方

コールバック関数は:

  1. CmdMod:command_init(InitArg) -> InitState (省略可能)
  2. CmdMod:command_fin(LastState) -> ok (省略可能)
  3. CmdMod:command(Cmd::atom(), Arg, State) -> {ok, NewState}|{quit, LastState} (必須)

CmdMod:command/3の第1引数以外の引数型は任意。

ミニシェルインタプリタ関数が {quit, LastState} を受け取ると、CmdMod:command_fin(LastState) を呼び出してからシェルを終了する。

なお、1文字のコマンド q はミニシェル側の組み込みとなっている。それ以外の組み込み関数はない。組み込みはたぶん増える、、そのうち。

コールバック関数を書くときの注意とコツ

ランタイムエラーで終了しないように、command/3 の最後の関数節に次を入れておくこと。

command(_, _, State) ->
  io:format("illegal command~n"),
  {ok, State}.

改行だけ、「空白+ターム」のコマンドを定義しておくと激しく便利

% ...
command('', Arg, State) ->
  case Arg of
    none -> % 改行だけ
      % ...
      {ok, NewState_1};
    SomePattern ->
      % ...
      {ok, NewState_2};
    % ...
  end;
command(_, _, State) ->
  io:format("illegal command~n"),
  {ok, State}.

今後

これをメンテナンスはしないような気がする。もっと別なモノを考えている。問題だと思っているのは、コマンドコールバック関数を書くのが面倒なこと。

orddictモジュールに対するフロントエンド。これで、orddictの使い方が練習できる。

%% -*- coding: utf-8 -*-

%% @doc orddictのテスト
-module(od_test).

% -export([]).
-compile(export_all).

%% プリティプリント
pp(OD) ->
  orddict:fold(
    fun(K, V, _A) ->
        io:format(" ~p => ~p~n", [K, V])
    end,
    ok,
    OD).

%% ヘルプ表示
help_str() ->
  (" ?          -- show this help\n"
   " .          -- dump\n"
   " a {K, V}   -- append K, V\n"
   " al {K, VL} -- append_list K, VL\n"
   " e K        -- erase K\n"
   " fe K       -- fetch K\n"
   " fk         -- fetch_keys\n"
   " f K        -- find K\n"
   " k K        -- is_key  K\n"
   " sz         -- size\n"
   " s {K, V}   -- store K, V\n"
   " x          -- exit\n"
   " ENTER      -- pretty-print\n"
   " SPACE {K, V} -- store K, V and pretty-print\n"
  ).

help() ->
  io:format(help_str()).

command_init(_Arg) ->
  io:format("~nThis is od_test. "
            "type '?' for help.~n~n"
           ),
  orddict:new().

command_fin(_LastState) ->
  ok. % do nothing

command('.', Arg, State) ->
  io:format("Dump: Arg = ~p, State = ~p~n", [Arg, State]),
  {ok, State};
command('?', _Arg, State) ->
  help(),
  {ok, State};
command(a, {K, V}, State) ->
  NewState = orddict:append(K, V, State),
  {ok, NewState};
command(al, {K, VL}, State) ->
  NewState = orddict:append_list(K, VL, State),
  {ok, NewState};
command(e, K, State) ->
  NewState = orddict:erase(K, State),
  {ok, NewState};
command(fe, K, State) ->
  V = orddict:fetch(K, State),
  io:format("value = ~p~n", [V]),
  {ok, State};
command(fk, _Arg, State) ->
  V = orddict:fetch_keys(State),
  io:format("value = ~p~n", [V]),
  {ok, State};
command(f, K, State) ->
  V = orddict:find(K, State),
  io:format("value = ~p~n", [V]),
  {ok, State};
command(k, K, State) ->
  V = orddict:is_key(K, State),
  io:format("value = ~p~n", [V]),
  {ok, State};
command(sz, _Arg, State) ->
  V = orddict:size(State),
  io:format("value = ~p~n", [V]),
  {ok, State};
command(s, {K, V}, State) ->
  NewState = orddict:store(K, V, State),
  {ok, NewState};
command(x, _Arg, State) ->
  {quit, State};
command('', Arg, State) ->
  case Arg of
    none -> % 改行だけ
      pp(State),
      {ok, State};
    {K, V} ->
      NewState = orddict:store(K, V, State),
      pp(NewState),
      {ok, NewState};
    Key ->
      V = orddict:find(Key, State),
      io:format("value = ~p~n", [V]),
      {ok, State}
  end;
command(_, _, State) ->
  io:format("illegal command~n"),
  {ok, State}.

minshのソース

%% -*- coding: utf-8 -*-

%% @doc ミニミニシェル.
%%
%% start(CmdMod) または start(CmdMod, InitArg) として起動する。
%% CmdMod はコマンドを実装しているコールバックモジュール名(アトム)、
%% InitArg はコールバックモジュール初期化に使う引数である。
%% InitArg が省略されるとアトム none が初期化に使われる。
%%
%% コールバック関数は:<br/>
%% <ol>
%% <li>CmdMod:command_init(InitArg) -> InitState (省略可能)</li>
%% <li>CmdMod:command_fin(LastState) -> ok (省略可能)</li>
%% <li>CmdMod:command(Cmd::atom(), Arg, State) -> {ok, NewState}|{quit, LastState} (必須)</li>
%% </ol>
%% CmdMod:command/3の第1引数以外の引数型は任意である。
%% 
%% インタプリタ関数が {quit, LastState} を受け取ると、
%% CmdMod:command_fin(LastState) を呼び出してからシェルを終了する。

-module(minsh).

-export([start/1, start/2]).

-define(SPACE, 32).
-define(NL, 10).
-define(PROMPT, "$ ").

%% @equiv start(CmdMod, none)
%% @spec (atom()) -> ok
start(CmdMod) ->
  start(CmdMod, none).

%% @doc シェルの開始
%% @spec (atom(), term()) -> ok
start(CmdMod, InitArg) ->
  io:format("~n*** Starting shell (command module: ~s) ***~n~n", [CmdMod]),
  InitState = init(CmdMod, InitArg),
  Prompt = "(" ++ atom_to_list(CmdMod) ++ ")" ++ ?PROMPT,
  shell_loop(CmdMod, InitState, Prompt).

%% @doc コマンドモジュール初期化
%% @spec (atom(), term()) -> term()
init(CmdMod, InitArg) ->
  try
    apply(CmdMod, command_init, [InitArg])
  catch
    error:undef ->
      none
  end.

%% @doc コマンドモジュールの後始末
%% @spec (atom(), term()) -> ok
finalize(CmdMod, LastState) ->
  try
    apply(CmdMod, command_fin, [LastState])
  catch
    error:undef ->
      ok
  end.

%% @doc シェルのループ
%% @spec (atom(), term(), string()) -> ok
shell_loop(CmdMod, State, Prompt) ->
  Line = io:get_line(Prompt),
  case interpret(Line, CmdMod, State) of
    {ok , NewState} ->
      shell_loop(CmdMod, NewState, Prompt);
    {quit, LastState} ->
      finalize(CmdMod, LastState),
      io:format("~n*** bye. ***~n"),
      ok % exit from the loop
  end.

%% @doc 入力行の解釈実行
%% @spec (string(), atom(), term()) -> {ok, Cmd, Arg} | {error, Msg}
%% where
%%   Cmd = atom(), Arg = term(), Msg = IOList
interpret(Line, CmdMod, State) ->  
  CmdTuple = make_cmd_tuple(Line),
  case CmdTuple of
    {ok, Cmd, Arg} ->
      exec_command(Cmd, Arg, CmdMod, State);
    {error, Msg} ->
      io:format("Error: ~s~n", [Msg]),
      {ok, State}
  end.

%% @doc 入力行から、コマンドアトムと引数タームを作る
%% @spec (string()) -> {ok, Cmd::atom(), Arg::term()} | {error, Msg}
%% where
%%  Msg = IOList
make_cmd_tuple(Line) ->
  {CmdStr, ArgStr} = split_line(Line),
  case parse_term(ArgStr) of
    {ok, Term} ->
      {ok, list_to_atom(CmdStr), Term};
    {error, Msg} ->
      {error, Msg}
  end.

%% @doc 入力行をコマンドと引数に分割する
%% @spec (string()) -> {string(), string()}
split_line(Line) ->
  % Line_1 = string:strip(Line),
  split_line(Line, []).

%% @doc split_line/1 の作業関数
%% @spec (Looking::string(), Looked::string()) -> {string(), string()}
split_line(_Looking = [Ch|Rest], Looked) ->
  if Ch == ?SPACE orelse Ch == ?NL ->
      {lists:reverse(Looked), Rest};
     true ->
      split_line(Rest, [Ch|Looked])
  end;
split_line([], Looked) ->
  {lists:reverse(Looked), ""}.

%% @doc 文字列を解析してタームを作る
%% @spec (string()) -> {ok, term()} | {error, Msg}
%% where
%%   Msg = IOList
parse_term(Str) ->
  case erl_scan:string(Str) of
    {ok, Tokens, _} ->
      FixedTokens = fix_tokens(Tokens),
      case erl_parse:parse_term(FixedTokens) of
        {ok, Term} ->
          {ok, Term};
        {error, {_Line, erl_parse, Msg}} ->
          {error, Msg}
      end;
    {error, ErrorInfo, _} ->
      Msg = erl_scan:format_error(ErrorInfo),
      {error, Msg}
  end.

%% @doc Tokensの末尾にドットがないときは付け足す.
%% Tokensに関しては erl_scan を参照。
%% @spec (Tokens) -> Tokens
fix_tokens([]) ->
  [{atom, 1, none}, {dot, 1}];
fix_tokens(Tokens) ->
  case lists:last(Tokens) of
    {dot, _} ->
      Tokens;
    _ ->
      lists:append(Tokens, [{dot, 1}])
  end.

%% コマンドは必ず ok, quit のいずれかを返す。
%% インタプリタ関数が quit を受け取るとシェルを終了する。

%% @doc コマンドの実行
%% @spec (Cmd::atom(), Arg::term(), CmdMod::atom(), State::term()) -> 
%%   {ok, State::term()} | {quit, LastState::term()}
exec_command(q, _Arg, _CmdMod, State) -> % q は組み込み
  {quit, State};
%exec_command('', _, State, _) -> % 改行だけはNOP
%  {ok, State};
exec_command(Cmd, Arg, CmdMod, State) ->
  Result = 
    try 
      apply(CmdMod, command, [Cmd, Arg, State])
    catch
      error:undef ->
        finalize(CmdMod, State),
        throw(cannot_find_command_mod_fun)
    end,
  case Result of
    {ok, _NewState} ->
      Result;
    {quit, _NewState} ->
      Result;
    _Other ->
      finalize(CmdMod, State),
      throw(illegal_command_result)
  end.

*1:別に民主党のファンじゃねーぞ。

ValentinaValentina2012/08/24 18:51This piece was cogent, well-witrten, and pithy.

hzkiyhzhzkiyhz2012/08/25 15:48ulmEt6 <a href="http://gcoiyuwzcreu.com/">gcoiyuwzcreu</a>

kpyqduozykpyqduozy2012/08/27 00:13KrkVK3 <a href="http://dmuynnkjqulo.com/">dmuynnkjqulo</a>

dajugzudajugzu2012/08/27 22:29aIT0Zi , [url=http://jjbybzuabnzb.com/]jjbybzuabnzb[/url], [link=http://dffjjwepljnx.com/]dffjjwepljnx[/link], http://nbseayfagmif.com/

2009-03-30 (月)

名前の文字列化 (プリプロセッサ機能)

| 09:21

便利なこともある。

-define(Str(X), ??X).
-define(NameValue(X), [??X, X]).

foo() ->
  Foo = 100,
  io:format("this is " ++ ?Str(foo) ++ ".\n"),
  io:format("~s = ~p~n", ?NameValue(Foo)).

あくまで事前の構文処理レベルだが。

Eシェルの変数を汚さない方法

| 09:21

Erlangにはlet式がない*1ので、シェル操作で不便 -- 変数束縛がトップレベルで残るんで、非常にしばしば、f(), f(X) などで消すことになる。

JavaScriptで、大域環境を汚さないために (function(){ ... })(); を使う。これと同じことをEShellからやってみた。

40> f().
ok
41> (fun()->   X = 1, io:format("Hello~n"), Y = 2,  ok end)().
Hello
ok
42> b().
ok
43> 

確かに変数束縛はできてない。便利かどうかはワカラン。つーか、便利じゃねーな。

*1:Core Erlangではあったかもしれない

2009-03-28 (土)

スケルトンの自動挿入

| 12:22

EDoc対応のスケルトンは次で示した。

OPTのオフィシャルアプリケーションを構成するモジュールのネーミングはほぼ習慣化している。(「ネーミング」参照。)

であるなら、ファイル名が *_server.erl ならgen_serverスケルトン、*_sup.erl ならsupervisorスケルトンを自動挿入すりゃいんじゃないの。なんで今までやってなかったの? アホッ>自分。

;; 自動的に挿入

;; 以下のディレクトリ指定で、'/'は必須、ないとダメ
(setq auto-insert-directory "~/lib/code-templates/") 
(auto-insert-mode 1)
(setq auto-insert-query nil) ;; デフォルトは 'function
(add-to-list 'auto-insert-alist
	     '("\\.erl$" . "erlang.erl"))
(add-to-list 'auto-insert-alist
	     '("\\.hrl$" . "erlang.hrl"))
(add-to-list 'auto-insert-alist
	     '("_server\\.erl$" . "gen_server_skelton.erl"))
(add-to-list 'auto-insert-alist
	     '("_sup\\.erl$" . "supervisor_skelton.erl"))

プリプロセッサマクロ

| 12:25

意外と柔軟性がある。次のようなこともできる。

-ifdef(debug).
  -define(atom_guard(X), when is_atom(X)).
  -define(atom_guard2(X, Y), when is_atom(X) and is_atom(Y)).
-else.
  -define(atom_guard(X),).
  -define(atom_guard2(X, Y),).
-endif.

foo(X, Y) ?atom_guard2(X, Y) ->
   ok.

アリティによるマクロオーバーロードはできない。

マクロはやりすぎるとろくなことはないけどね。-define(else, true).なんてのもやってみたけど、賛否が分かれる使い方だろう。

エラーメッセージ用コードのスケルトンを作る

| 12:48

「例外で投げるReasonデータについて考える」で紹介した方法はなかなか具合がいい。不慮の事故、予期せぬ災害というほどのことではないが値が出せないときは、キャッチされるのを期待してユーザー定義の例外を投げるとよい。

while(<>) {
    if (/^\s*throw\s*\(\s*{\s*{\s*([^\s]+)\s*,\s*(.+)}\s*,\s*(.+)}\s*\)/) {
	print "message($2, $3) ->\n  \"\".\n";
    }
}

これで、format_errorのメッセージ部分のスケルトンができる。Perlは滅多に使わないけど正規表現ツールには使う。そういや、ErlangにもPCREが付いたんだな。