Hatena::Grouperlang

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

2009-06-02 (火)

コマンドラインなら1行なのに、、、

| 10:58

とあるディレクトリから下に、サイズ1メガ(1024*1024 = 1048576 ≒ 1000000)以上のファイルがあるかどうかを調べる必要があった。

$ find the/dir -type f -size +1000000c -print

で出来た。

が、なぜか(なぜだ?)Erlangでも書いてみようと思って、無駄に丁寧に書いた。あー無駄だ。

100> check_size:traverse("the/dir", fun check_size:check_size/4, [1000000]).

↑で、先のfindコマンドとほぼ同じ。

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

-module(check_size).
-compile(export_all).

% @doc ファイルの種類を返す.
% @spec (string()) -> (not_exit | dir | regular | special)
file_type(Filename) ->
  case filelib:is_file(Filename) of
    false ->
      not_exist;
    true ->
      case filelib:is_dir(Filename) of
        true ->
          dir;
        false ->
          % regularとspecialの区別はあんまり当てにならないみたい
          case filelib:is_regular(Filename) of
            true ->
              regular; 
            false ->
              special
          end
      end
  end.

% @equiv traverse(TopDir, Action, [])
traverse(TopDir, Action) ->
  traverse(TopDir, Action, []).

% @doc 起点となるディレクトリから再帰的にディレクトリツリーをたどり、
% 各ファイルに対して指定されたアクションを実行する.
%
% == アクション関数の仕様 ==
% <dl>
% <dt>第1引数</dt><dd>ディレクトリ名</dd>
% <dt>第2引数</dt><dd>ファイルベース名</dd>
% <dt>第3引数</dt><dd>ファイルのタイプ (not_exit | dir | regular | special)</dd>
% </dl>
% 
% 他に追加の引数をいくつでも付けてよい. 
% 追加引数は、リストにまとめて traverse の第3引数に渡す.


% @spec (string(), function(), [any()]) -> ok
traverse(TopDir, Action, MoreArgs) ->
  {ok, Names} = file:list_dir(TopDir),
  lists:foreach(fun (Name) -> 
                    do_action(TopDir, Name, Action, MoreArgs)
                end,
                Names).

% @doc 関数として渡されたアクションを、
% 指定のディレクトリとファイル名(basename)に対して実行する.
% @spec (string(), string(), function(), [any()]) -> ok
do_action(Dir, Name, Action, MoreArgs) ->
  Filename = filename:join(Dir, Name),
  Type = file_type(Filename),
  Fullargs = [Dir, Name, Type | MoreArgs],
  case Type of
    dir ->
      apply(Action, Fullargs),
      traverse(Filename, Action, MoreArgs);
    _Other ->
      apply(Action, Fullargs),
      ok
  end.

% @doc traverseのアクション関数、ファイルサイズのチェック.
% @spec (string(), string(), atom(), number()) -> ok
check_size(Dir, Name, Type, Limit) ->
  File = filename:join(Dir, Name),
  case Type of
    regular ->
      Size = filelib:file_size(File),
      if
        Size >= Limit ->
          io:format("!! ~s ~p~n", [File, Size]);
        true ->
          ok
      end;
    dir ->
      io:format("[~s]~n", [File]);
    special ->
      ok
  end.

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).

YAWSのクッキーセッション

| 13:47

yaws_apiにあるいくつかの関数を使う。説明をそのうち書くだろう、たぶん。

-record(myopaque, {udata,
                   times = 0,
                   foobar}).

out(A) ->
    H = A#arg.headers,
    C = H#headers.cookie,
    case yaws_api:find_cookie_val("baz", C) of
        [] ->
            M = #myopaque{},
            Cookie = yaws_api:new_cookie_session(M),
            Data = {ehtml,
                    {html,[],
                     ["I just set your cookie to ", Cookie, "Click ",
                      {a, [{href,"session1.yaws"}], " here "},
                      "to revisit"]}},
            CO = yaws_api:setcookie("baz",Cookie,"/"),
            [Data, CO];
        Cookie ->
            {ok, OP} = yaws_api:cookieval_to_opaque(Cookie),
            OP2 = OP#myopaque{times = OP#myopaque.times + 1},
            yaws_api:replace_cookie_session(Cookie, OP2),
            Data = {ehtml,
                    {html,[],
                     [
                      "Click ",
                      {a, [{href,"session1.yaws"}], " here "},
                      "to revisit",
                      {p, [], f("You have been here ~p times", [OP2#myopaque.times])},
                      {p, [], f("Your cookie is ~s", [Cookie])}]}},
            Data
    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/

2008-12-25 (木)

helloをちゃんと作る (2) サーバー

| 08:54

helloをgen_severにした。hello_serverからhello_implを呼んでいる。EDocがちゃんとドキュメントを出すようにした。が、英語と日本語がマゼマゼの事態となった;まっ、いいとしよう。

%% hello_server.erl -*- coding: utf-8 -*-

%% @doc helloアプリケーションサーバー.
-module(hello_server).
-behaviour(gen_server).

-export([ % サーバーの開始
          start_link/0, start/0
        ]).
-export([ % gen_serverのコールバック関数
          init/1, 
          handle_call/3, handle_cast/2, handle_info/2,
          terminate/2, code_change/3
        ]).

-define(SERVER, ?MODULE).

%%====================================================================
%% API
%%====================================================================

%% @doc サーバーをリンクしてスタートさせる.
%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
%%       where 
%%         Pid = pid()
%%         Error = term()
%%
start_link() ->
  gen_server:start_link({local, ?SERVER}, % サーバーの登録名
                        ?MODULE,          % コールバックモジュール名(アトム)
                        none,             % init/1 への引数
                        []                % オプションリスト
                        ).

%% @doc サーバースタンドアローンでスタートさせる.                       ). 
%% @spec () -> {ok,Pid} | ignore | {error,Error}
start() ->
  gen_server:start({local, ?SERVER}, ?MODULE, none, []).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%% @private
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%%        where
%%          Args = term()
%%          State = term()
%%          Timeout = integer() | infinity
%%          Reason = term()
%%
%% @doc Initiates the server
init(none) ->
  io:fwrite("Start ~s (~p)~n", [?SERVER, self()]),
  {ok, no_state}. % 状態は使わないのでno_state

%% @private
%% @spec handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                            {reply, Reply, State, Timeout} |
%%                                            {noreply, State} |
%%                                            {noreply, State, Timeout} |
%%                                            {stop, Reason, Reply, State} |
%%                                            {stop, Reason, State}
%%       where
%%         Request = term()
%%         From = {pid(), reference()}
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%% 
%% @doc Handling call messages
handle_call(hello, _From, State) ->
  Reply = hello_impl:hello(),
  {reply, Reply, State};
handle_call({hello, Whom}, _From, State) ->
  Reply = hello_impl:hello(Whom),
  {reply, Reply, State};

%% 以下、実験のため;sleep/1, crush/0
handle_call({sleep, MilliSec}, _From, State) ->
  timer:sleep(MilliSec),
  Reply = ok,
  {reply, Reply, State};
handle_call(crush, _From, _State) ->
  erlang:error(crush);
%% その他のcall
handle_call(Request, From, State) ->
  error_logger:warning_msg("unexpected call: call-requet=~p, from=~p~n", 
                            [Request, From]),
  Reply = {error, not_supported},
  {reply, Reply, State}.

%% @private
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%%                                  {noreply, State, Timeout} |
%%                                  {stop, Reason, State}
%%       where
%%         Msg = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%%
%% @doc Handling cast messages
handle_cast(stop, State) ->
  {stop, normal, State};
handle_cast(_Msg, State) ->
  error_logger:warning_msg("unexpected cast: cast-messaget=~p~n", 
                            [_Msg]),
  {noreply, State}.

%% @private
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%%       where
%%         Info = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%% @doc Handling all non call/cast messages
handle_info(_Info, State) ->
  error_logger:warning_msg("unexpected info: info-messaget=~p~n", 
                            [_Info]),
  {noreply, State}.

%% @private
%% @spec terminate(Reason, State) -> void()
%%       where
%%         Reason = normal | shutdown | term()
%%         State = #state{}
%%
%% @doc This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
terminate(_Reason, _State) ->
  io:fwrite("Stop ~s (~p)~n", [?SERVER, self()]),
  ok.

%% @private
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%%       where
%%         OldVsn = term() | {down, term()}
%%         State = #state{}
%%         Extra = term()
%%         NewState = #state{}
%%
%% @doc Convert process state when code is changed
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

今回使ったスケルトン

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

%%%-------------------------------------------------------------------
%%% @author Your Name <your@mail.address>
%%% @doc Module Description
%%%
-module(gen_server_edoc_skelton).

-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

%% @type void() = ok | void | none

%% @type state() = #state{}
%%
-record(state, {}).

%%====================================================================
%% API
%%====================================================================

%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
%%       where 
%%         Pid = pid()
%%         Error = term()
%% 
%% @doc Starts the server
start_link() ->
  gen_server:start_link({local, ?SERVER}, ?MODULE, , ).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%% @private
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%%        where
%%          Args = term()
%%          State = #state{}
%%          Timeout = integer() | infinity
%%          Reason = term()
%%
%% @doc Initiates the server
init([]) ->
  {ok, #state{}}.

%% @private
%% @spec handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                            {reply, Reply, State, Timeout} |
%%                                            {noreply, State} |
%%                                            {noreply, State, Timeout} |
%%                                            {stop, Reason, Reply, State} |
%%                                            {stop, Reason, State}
%%       where
%%         Request = term()
%%         From = {pid(), reference()}
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%% 
%% @doc Handling call messages
handle_call(_Request, _From, State) ->
  Reply = ok,
  {reply, Reply, State}.

%% @private
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%%                                  {noreply, State, Timeout} |
%%                                  {stop, Reason, State}
%%       where
%%         Msg = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%%
%% @doc Handling cast messages
handle_cast(_Msg, State) ->
  {noreply, State}.

%% @private
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%%       where
%%         Info = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%% @doc Handling all non call/cast messages
handle_info(_Info, State) ->
  {noreply, State}.

%% @private
%% @spec terminate(Reason, State) -> void()
%%       where
%%         Reason = normal | shutdown | term()
%%         State = #state{}
%%
%% @doc This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
terminate(_Reason, _State) ->
  ok.

%% @private
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%%       where
%%         OldVsn = term() | {down, term()}
%%         State = #state{}
%%         Extra = term()
%%         NewState = #state{}
%%
%% @doc Convert process state when code is changed
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

%% the end

helloをちゃんと作る (3) ネーミング

| 09:08

まずネーミングの一般論:

App.erl アプリケーションインターフェースモジュール
App_api.erl 公開する関数を集めたインターフェースモジュール(必要なら)
App_app.erl applicationコールバックモジュール
App_server.erlgen_serverコールバックモジュール
App_X_srv.erl サーバーが複数の場合の、gen_serverコールバックモジュール
App_sup.erl トップスーパーバイザ
App_Y_sup.erl 中間のスーパーバイザ
App_impl.erl App_serverの実装モジュール(必要なら)
App_X_impl.erl App_X_srvの実装モジュール(必要なら)
App_lib.erl App_implと同じ目的だが、これだけを外部から使える関数
App_util.erAppで使う便利関数、小物など

helloで作るのは:

hello.erl アプリケーションインターフェースモジュール
hello_api.erl 公開する関数を集めたインターフェースモジュール
hello_app.erl applicationコールバックモジュール
hello_server.erlgen_serverコールバックモジュール
hello_sup.erl トップスーパーバイザ
hello_impl.erl hello_serverの実装モジュール

hello_apiにhello/0, hello/1を入れる。helloにはstart/0とstop/0、かな。

RianRian2012/10/08 11:58Unbelievable how well-wtirten and informative this was.

rvdafpshahvrvdafpshahv2012/10/09 05:04bTh7lr <a href="http://arcnhmnoyjii.com/">arcnhmnoyjii</a>

milrymqyhoamilrymqyhoa2012/10/09 14:38buCp2o , [url=http://jkemnosbgpsu.com/]jkemnosbgpsu[/url], [link=http://adihidisecpf.com/]adihidisecpf[/link], http://anjyysqtupdt.com/

nffnkgikysnffnkgikys2012/10/10 22:38w4LY0q <a href="http://zbewoilgxxbk.com/">zbewoilgxxbk</a>

jbkrktjbkrkt2012/10/12 02:35qmVN40 , [url=http://gsvebelbjsvg.com/]gsvebelbjsvg[/url], [link=http://ivlknodqlxwf.com/]ivlknodqlxwf[/link], http://qpktraapruqf.com/

2008-12-19 (金)

寛容なコール

| 09:43

関数実行(評価)のバリエーションを確認する意味で:

%% @type funlike() = function() 
%%                 | atom() 
%%                 | {atom(), atom()}
%%                 | {function(), list()}
%%                 | {atom(), list()}
%%                 | {atom(), atom(), list()}
%%                 | cons(atom(), cons(atom(), list())) .
%% 関数と思えるモノ

%% @doc 関数と思えるモノをできるだけ寛容に実行
%% @spec (funlike()) -> term()

%% 引数なし
tolerant_call(Fun) when is_function(Fun) ->
  Fun();
tolerant_call(FunName) when is_atom(FunName) ->
  apply(?MODULE, FunName, []);
tolerant_call({Mod, FunName}) when is_atom(Mod), is_atom(FunName) ->
  apply(Mod, FunName, []);

%% 引数あり
tolerant_call({Fun, Args}) when is_function(Fun), is_list(Args) ->
  apply(Fun, Args);
tolerant_call({FunName, Args}) when is_atom(FunName), is_list(Args) ->
  apply(?MODULE, FunName, Args);
tolerant_call({Mod, FunName, Args}) when is_atom(Mod), is_atom(FunName), is_list(Args) ->
  apply(Mod, FunName, Args);
tolerant_call([Mod, FunName |Args]) when is_atom(Mod), is_atom(FunName) -> 
  apply(Mod, FunName, Args).

2008-12-18 (木)

無限大を含む整数算術

| 15:05

必要性があったので書いてみた。

  • 確かに、こういうのはErlang向きだろう。表を見ながらそのまま引き写すだけ。
  • だが、表を見ながらの転記は、退屈でつまんなくて、そのくせ注意力がいる。
  • ので、えれーツマンネー。辛い。Erlangでも辛い!
  • ライブラリ設計として、考えるべき点も二三はあるのだが、目が疲れたから後にする。

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

%% @doc 正負の無限大を含む整数の算術
-module(x_arith).

-export([ % 定数
          infinity/0, negative_infinity/0
         ]).
-export([ % 各種の述語
         is_x_integer/1, is_x_positive/1, is_x_nonnegative/1,
         is_finite/1, is_not_finite/1, is_infinite/1, is_negative_infinite/1
        ]).
-export([ % 加減乗除、符号反転
         x_add/2, x_sub/2, x_mul/2, x_div/2, x_neg/1
        ]).
-export([ % エラー情報の可読文字列化
          format_error/1
         ]).

%% マクロ定数定義
-define(INF, infinity).
-define(NEGINF, negative_infinity).

%% @type x_integer() = integer() | infinity | negative_infinity .
%% 正負の無限大を含む拡張整数

%% @type x_arith_excep() = {{Mod, Id}, Hint}
%% where
%%   Mod = atom()
%%   Id = atom()
%%   Hint = any().
%% 例外として投げるデータ.

%% @doc 正の無限大(定数)
%% @spec () -> atom()
infinity() ->
  ?INF.

%% @doc 負の無限大(定数)
%% @spec () -> atom()
negative_infinity() ->
  ?NEGINF.

%% @doc 拡張整数か
%% @spec (any()) -> bool()
is_x_integer(X) ->
  is_integer(X) orelse X =:= ?INF orelse X =:= ?NEGINF.

%% @doc 拡張正整数か
%% @spec (any()) -> bool()
is_x_positive(X) ->
  (is_integer(X) andalso X > 0) orelse X =:= ?INF.

%% @doc 拡張非負整数か
%% @spec (any()) -> bool()
is_x_nonnegative(X) ->
  (is_integer(X) andalso X >= 0) orelse X =:= ?INF.

%% @doc 有限整数か
%% @spec (any()) -> bool()
is_finite(X) ->
  is_integer(X).

%% @doc 非有限整数(正負の無限大)か
%% @spec (any()) -> bool()
is_not_finite(X) ->
  X =:= ?INF orelse X =:= ?NEGINF.

%% @doc 正の無限大か
%% @spec (any()) -> bool()
is_infinite(X) ->
  X =:= ?INF.

%% @doc 負の無限大か
%% @spec (any()) -> bool()
is_negative_infinite(X) ->
  X =:= ?NEGINF.

%%%%%%
%% addition, subtraction, multiplication and division
%%%%%% 

%% 場合分け:
%%        X=NEGINF, X=INF X=other 
%% Y=NEGINF  1      4       7
%% Y=INF     2      5       8
%% Y=other   3      6       9

%% @doc 足し算
%% @spec (X::x_integer(), Y::x_integer()) -> x_integer()
%% @throws x_arith_excep()

x_add(?NEGINF, ?NEGINF) -> % 1
  ?NEGINF;
x_add(?NEGINF, ?INF) -> % 2
  throw({{?MODULE, undef}, none});
x_add(?NEGINF, Y) when is_integer(Y) -> % 3
  ?NEGINF;

x_add(?INF, ?NEGINF) -> % 4
  throw({{?MODULE, undef}, none});
x_add(?INF, ?INF) -> % 5
  ?INF;
x_add(?INF, Y) when is_integer(Y) -> %6
  ?INF;

x_add(X, ?NEGINF) when is_integer(X) -> %7
  ?NEGINF;
x_add(X, ?INF) when is_integer(X) -> %8
  ?INF;
x_add(X, Y) when is_integer(X), is_integer(Y) -> %9
  X + Y.

%% @doc 引き算
%% @spec (X::x_integer(), Y::x_integer()) -> x_integer()
%% @throws x_arith_excep()

x_sub(?NEGINF, ?NEGINF) -> % 1
  throw({{?MODULE, undef}, none});  
x_sub(?NEGINF, ?INF) -> %2
  ?NEGINF;  
x_sub(?NEGINF, Y) when is_integer(Y) -> %3
  ?NEGINF;

x_sub(?INF, ?NEGINF) -> % 4
  ?INF;
x_sub(?INF, ?INF) -> % 5
  throw({{?MODULE, undef}, none});
x_sub(?INF, Y) when is_integer(Y) -> % 6
  ?INF;

x_sub(X, ?NEGINF) when is_integer(X) -> %7
  ?INF;
x_sub(X, ?INF) when is_integer(X) -> %8
  ?NEGINF;
x_sub(X, Y) when is_integer(X), is_integer(Y) -> %9
  X - Y.

%% 場合分け:
%% 印(*, #, /)が付いているケースはまとめて処理する
%%        X=NEGINF,X=INF, X<0, X=0, X>0, 
%% Y=NEGINF  1      6     11   16#  21
%% Y=INF     2      7     12   17#  22
%% Y<0       3      8     13/  18#  23/
%% Y=0       4*     9*    14*  19#  24*
%% Y>0       5     10     15/  20#  25/

%% @doc 掛け算
%% @spec (X::x_integer(), Y::x_integer()) -> x_integer()

x_mul(0, Y) -> % 16, 17, 18, 19, 20
  case is_x_integer(Y) of
    true ->
      0
  end;
x_mul(X, 0) -> % 4, 9, 14, 24
  case is_x_integer(X) of
    true ->
      0
  end;
x_mul(?NEGINF, ?NEGINF) -> % 1
  ?INF;
x_mul(?NEGINF, ?INF) -> % 2
  ?NEGINF;
x_mul(?NEGINF, Y) when is_integer(Y), Y < 0 -> % 3
  ?INF;
%x_mul(?NEGINF, 0) ->
%  0;
x_mul(?NEGINF, Y) when is_integer(Y), Y > 0 -> % 5
  ?NEGINF;

x_mul(?INF, ?NEGINF) -> % 6
  ?NEGINF;
x_mul(?INF, ?INF) -> % 7
  ?INF;
x_mul(?INF, Y) when is_integer(Y), Y < 0 -> % 8
  ?NEGINF;
%x_mul(?INF, 0) ->
%  0;
x_mul(?INF, Y) when is_integer(Y), Y > 0 -> % 10
  ?INF;

x_mul(X, ?NEGINF) when is_integer(X), X < 0 -> % 11
  ?INF;
x_mul(X, ?INF) when is_integer(X), X < 0 -> % 12
  ?NEGINF;

x_mul(X, ?NEGINF) when is_integer(X), X > 0 -> % 21
  ?NEGINF;
x_mul(X, ?INF) when is_integer(X), X > 0 ->  % 22
  ?INF;
x_mul(X, Y) when is_integer(X), is_integer(Y) -> % 13, 15, 23, 25
  X * Y.

%% @doc 割り算
%% @spec (X::x_integer(), Y::x_integer()) -> x_integer()
%% @throws x_arith_excep()

x_div(0, Y) -> % 16, 17, 18, 19, 20
  case is_x_integer(Y) of
    true ->
      0
  end;
x_div(X, 0) -> % 4, 9, 14, 24
  case is_x_integer(X) of
    true ->
      throw({{?MODULE, devided_by_zero}, none})
  end;

x_div(?NEGINF, ?NEGINF) -> % 1
  throw({{?MODULE, undef}, none});
x_div(?NEGINF, ?INF) -> % 2
  throw({{?MODULE, undef}, none});
x_div(?NEGINF, Y) when is_integer(Y), Y < 0 -> % 3
  ?INF;
%x_div(?NEGINF, 0) ->
%  0;
x_div(?NEGINF, Y) when is_integer(Y), Y > 0 -> % 5
  ?NEGINF;

x_div(?INF, ?NEGINF) -> % 6
  throw({{?MODULE, undef}, none});
x_div(?INF, ?INF) -> % 7
  throw({{?MODULE, undef}, none});
x_div(?INF, Y) when is_integer(Y), Y < 0 -> % 8
  ?NEGINF;
%x_div(?INF, 0) ->
%  0;
x_div(?INF, Y) when is_integer(Y), Y > 0 -> % 10
  ?INF;

x_div(X, ?NEGINF) when is_integer(X)  -> % 11, 12
  throw({{?MODULE, undef}, none});
x_div(X, ?INF) when is_integer(X)  -> % 21, 22
  throw({{?MODULE, undef}, none});
x_div(X, Y) when is_integer(X), is_integer(Y) -> % 13, 15, 23, 25
  X / Y.

%% @doc 符号反転
%% @spec (X::x_integer()) -> x_integer()
x_neg(?NEGINF) ->
  ?INF;
x_neg(?INF) ->
  ?NEGINF;
x_neg(X) when is_integer(X) ->
  -X.

%% @doc エラー情報の可読文字列化
%% @spec (x_arith_excep() | atom()) -> string()
format_error({{?MODULE, Id}, _Hint}) ->
  message(Id);
format_error(Id) when is_atom(Id) ->
  message(Id).

%% @spec (atom()) -> string()
message(undef) ->
  "operation undefined";
message(devided_by_zero) ->
  "devided by zero".

RasadRasad2012/02/09 19:39So much info in so few words. Tosotly could learn a lot.

fttosrpfqrfttosrpfqr2012/02/09 22:04xtmx4I <a href="http://dcwmuvowvxrk.com/">dcwmuvowvxrk</a>

qbwziqzqbwziqz2012/02/11 03:45EuHBqE , [url=http://vmxmmwylocyl.com/]vmxmmwylocyl[/url], [link=http://ewgoxruwespg.com/]ewgoxruwespg[/link], http://oyqkypsxrner.com/

zygmbyzygmby2012/02/12 23:23Gr6C5f <a href="http://foeilrknewxy.com/">foeilrknewxy</a>

iigwhkksnsiigwhkksns2012/02/14 01:05eCy3ac , [url=http://boynrsvpwqyg.com/]boynrsvpwqyg[/url], [link=http://dbgndaxgwozh.com/]dbgndaxgwozh[/link], http://pqhcxmcmdrlj.com/

2008-12-11 (木)

helloをちゃんと作る

| 08:44

HelloWorldを無駄にちゃんと作ってみるシリーズ。

とりあえずはコレだけ、つまんねー。

%% hello_impl.erl  -*- coding: utf-8 -*-

%% @doc Helloアプリケーションの実装モジュール.
-module(hello_impl).
-export([hello/0, hello/1]).

%% @doc デフォルトの相手に対してhelloと言う.
%% @spec () -> ok
hello() ->
  hello("world").

%% @doc 引数で指定された相手に対してhelloと言う.
%% @spec (string()) -> ok
hello(Whom) when is_list(Whom) -> 
  io:fwrite("Hello, ~s.~n", [Whom]).

サーバーとかスーパーバイザとか作っていく、たぶん。

VoluntasVoluntas2008/12/17 17:08すーぱーばいざーつきの hello world に期待。

m-hiyamam-hiyama2008/12/17 18:08Voluntasさん、
はい、そのうち :-)