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

2012年3月4日日曜日

Erlang crypto rand_uniform サンプル

タイトル通り crypto:rand_uniform のサンプルを。

1番苦労したのは、モジュールのロードでした。

普通にやっていたら、「定義されていない関数だ」みたいなエラーがでました。

そこを踏まえ、サンプルの提示です。

◆モジュールのロード

ソースのコンパイル時にオプションを付ける。

使用したいモジュールが使えるようになる。

今回はcryptoモジュールなので、そのbeamがある場所を-pzのあとに指定する。

$> erlc -pz C:/erl5.9/lib/crypto-2.1/ebin test_crypto_uniform.erl

あとは、コンパイルしたモジュールを実行する。

$> erl -noshell -run test_crypto_uniform start -run init stop

◆ソース

-module(test_crypto_uniform).
-compile([export_all]).
%-import( crypto, [ rand_uniform/2 ] ).

start() ->
    Rand = crypto:rand_uniform(100, 10000),
    io:format( "rand:~p~n", [Rand] ),
    timer:sleep( Rand ).

Erlang ロード モジュール 検索パス 設定

Erlangのリファレンスには、ちゃんとモジュールがあるが、実際に動かそうとすると、そんなモジュールないよと怒られる。

そんなときにはどうするか。
モジュールをロードしよう。

◆Erlangシェルの場合
.(ピリオド)erlangに設定する


Erlangシェルを起動したときの表示から、モジュールがロードされていることがわかる(?)


◆コマンド実行におけるロード
erlcコマンドのオプションで -pz PATH または -pa PATH をつける


(上記画像はMakefileに記述している。)

2012年3月3日土曜日

Erlang プロセス 生成 サンプル

Rubyの勉強をしている。

そのとき、スレッドを並行実行するサンプルを丸写しした。

そのサンプルをErlang版として記述した。

以下にそれをコピペしておく。

◆並行実行動作

1.2つのプロセスが生成される。
2.それぞれのプロセスはある時間(秒)スリープして、時間経過後、ある文字を標準出力する。
3.7秒後メインプロセスが終了する(生成したプロセスも終了する)。

◆ソース

-module(test_thread_start).
-compile(export_all).

%% 標準出力
log_print( Start_time, String ) ->
    { _, Now, _ } = erlang:now(),
    Time = Now - Start_time,
    io:format( "~ps: ~s~n", [ Time, String ] ).

%% プロセス生成
loop_thread( Start_time, Sec, Str ) ->
    spawn( test_thread_start, thread, [ Start_time, Sec, Str ] ).

%% 生成したプロセスの動作
thread( Start_time, Wait_time, String ) ->
    timer:sleep( Wait_time ),
    log_print( Start_time, String ),
    thread( Start_time, Wait_time, String ).

start() ->
    { _, Start_time, _ } = erlang:now(),
    log_print( Start_time, "Start" ),

    loop_thread( Start_time, 2000, "a" ),
    loop_thread( Start_time, 3000, "b" ),

    timer:sleep( 7000 ),
    log_print( Start_time, "End" ).

◆実行結果


2012年2月29日水曜日

Erlang peername 接続相手のIPとPort

peername:接続元のIPとPort番号を返却する

peername(Socket) -> {ok, {Address, Port}} | {error, posix()}

Types:
Socket = socket()
Address = ip_address()
Port = integer() >= 0

Returns the address and port for the other end of a connection.

サンプル:
(サーバ)
1回だけクライアントと接続します。そのとき、接続元のクライアントのIPアドレスとポート番号を表示する。

(クライアント)
サーバへ1回だけ接続する。そのとき、サーバから受信したデータを表示する。サンプルでは「Hello」固定。

【サーバ】
-module( sock_srv_accept_info ).
-compile( export_all ).

server() ->
    %% TCPクライアントからの接続要求を待てる状態
    { ok, LSock } = gen_tcp:listen( 12345, [ binary, { packet, 0 } ] ),
    %% TCPクライアントからの接続要求を受け付ける
    { ok, ASock } = gen_tcp:accept( LSock ),

    io:format( "accept:~p~n", [ inet:peername( ASock ) ] ),       % 接続元のIPとポート番号表示
    %% 送信
    gen_tcp:send( ASock, "Hello" ),
    %% TCPセッション終了
    ok = gen_tcp:close( ASock ).

【クライアント】
-module( sock_client ).
-compile( export_all ).

client() ->
    %% サーバ接続先
    Server = "localhost",
    %% サーバに接続
    { ok, Sock } = gen_tcp:connect( Server,
                                    12345,
                                    [ binary, { packet, 0} ] ),
    %% サーバからデータ受信
    %receive
    %    { tcp, Sock, Bin } ->
    %        io:format( "Client get:~p~n", [ binary:bin_to_list( Bin ) ] )
    %end,
    %% サーバからデータ受信
    { ok, Packet } = gen_tcp:recv( Sock, 0 ),
    io:format( "time:~p~n", [ Packet ] ),
    %% ソケットの終了
    gen_tcp:close( Sock ).

2012年2月26日日曜日

Erlang timer server

下記URLにあるタイマーサーバを真似てみた。
Erlang Programming Language : http://www.erlang.org/article/15

サーバと通信するクライアントも作った。

サーバは現在日付と時刻を返す。
クライアントはそれを表示する。

ただひとつ問題があった。Vistaだからかもしれないが、gen_tcp:recv()でパケットを受信しようとすると、エラールートに入ってしまう。いろいろ調べてみたけど、よくわからなかった。Winsock が原因じゃないかと思うけど。

◆サーバ

-module(time_srv).
-compile(export_all).

%%start( Pno ) ->
%%    spawn( ?MODULE, loop0, [ Pno ] ).
start() ->
    %%spawn( ?MODULE, loop0, [ 12346 ] ).
    loop0( 12346 ).

loop0( Port ) ->
    case gen_tcp:listen( Port, [ binary, { packet, 0 }, { active, false } ] ) of
        { ok, LSock } ->
            io:format("listen success~n"),
            loop( LSock );
        _ ->
            io:format("listen failed~n"),
            stop
   
    end.

loop( Listen ) ->
    io:format("loop start.~n"),
    case gen_tcp:accept( Listen ) of
        { ok, S } ->
            io:format("accept success~n"),
            gen_tcp:send( S, io_lib:format( "~p~n", [ { date(), time() } ] ) ),
            io:format("send success~n"),
            gen_tcp:close( S ),
            loop( Listen );
        _ ->
            io:format("accept failed~n"),
            loop( Listen )
    end.


◆クライアント
-module( time_cli ).
-compile( export_all ).

client() ->
    %% サーバ接続先
    Server = "localhost",
    %% サーバに接続
    { ok, Sock } = gen_tcp:connect( Server,
                                    12346,
                                    [ binary, { packet, 0} ] ),
    %% サーバからデータ受信
    do_recv( Sock ),
    %% ソケットの終了
    gen_tcp:close( Sock ).

do_recv( Sock ) ->
    receive
        { tcp, Sock, Pct } ->
             io:format( "time:~p~n", [ Pct ] );
        _ -> io:format( "error" )
    end.

2012年2月25日土曜日

Erlang gen_tcp:connect { active, false } オプション

tcpの勉強をしている。
Cのプログラムを組んで、Erlangでも試してみることをしている。

そこで、Erlangのオンラインマニュアルにあるサンプルを動かしてみた。
Erlang -- gen_tcp : http://www.erlang.org/doc/man/gen_tcp.html

connect() のオプション{ active, false }について、試したことを書き記しておく。

{ active, false }の有無で、データ受信の方法が変わる。
以下にまとめたものを示す。
受信の対応を間違えると落ちます。crash_dump吐きます。
以下にソースを記す。
コメントアウトしているところが、{ active, false }ありのバージョン。

◆クライアント

-module(sample_client).
-compile(export_all).

client() ->
    SomeHostInNet = "localhost",
    { ok, Sock } = gen_tcp:connect( SomeHostInNet, 5678,
                                    [ binary, { packet, 0 } ] ),
    ok = gen_tcp:send( Sock, "Some Data" ),
    ok = gen_tcp:close( Sock ).

◆サーバ

-module(sample_server).
-compile(export_all).

server() ->
    %{ ok, LSock } = gen_tcp:listen( 5678,
     %                               [ binary, { packet, 0 }, { active, false } ] ),
    { ok, LSock } = gen_tcp:listen( 5678,
                                    [ binary, { packet, 0 } ] ),
    { ok, Sock } = gen_tcp:accept( LSock ),
    { ok, Bin } = do_recv( Sock, [] ),
    ok = gen_tcp:close( Sock ),
    io:format( "~p~n", [ Bin ] ).

%do_recv( Sock, Bs ) ->
%    case gen_tcp:recv( Sock, 0 ) of
%        { ok, B } ->
%            do_recv( Sock, [ Bs, B ] );
%        { error, closed } ->
%            { ok, list_to_binary( Bs ) }
%    end.
do_recv( Sock, Bs ) ->
    receive
        { tcp, Sock, B } ->
            do_recv( Sock, [ Bs, B ] );
        { tcp_closed, Sock } ->
            { ok, list_to_binary( Bs ) };
        { tcp_error, Sock, Reason } ->
            io:format( "recv error:~p~n", [ Reason ] ),
            { error, Sock, Reason }
    end.

動かし方
サーバを動かした後、クライアントを動かす。
◆コンパイル
$> erlc sample_server.erl
$> erlc sample_client.erl

◆起動
サーバとクライアントは別端末でやる
$> erl -noshell -run sample_server server -run init stop
<<"Some Data">>
(他端末で)
$> erl -noshell -run sample_client client -run init stop


2012年2月22日水曜日

Erlang TCP接続2 HTTPサーバ(超簡易)

TCPの勉強のつづき。

ローカル上でHTTPサーバを立てて、ブラウザで「Hello」と表示する。
C言語で実装したものをErlangでやってみた。

ソースを貼るが、もっとわかりやすいソースがWebにあったので、そちらをまず紹介。
Erlang で httpd - プログラマのネタ帳 : http://d.hatena.ne.jp/shomah4a/20110110/1294634635

下のソースを以下のようにビルドして、実行しよう。

$>C:\Users\andre\ework>erlc http_server.erl
c:/Users/andre/ework/http_server.erl:35: Warning: variable 'Result' is unused

$>C:\Users\andre\ework>erl -noshell -run http_server server -run init stop

◆ソース


-module( http_server ).
-compile( export_all ).

server() ->
    io:format("~p~n", [?LINE]),
    %% TCPクライアントからの接続要求を待てる状態
    { ok, LSock } = gen_tcp:listen( 12345, [ binary, { packet, 0 } ] ),

    io:format("~p~n", [?LINE]),
    Responce = "HTTP/1.0 200 OK\r\n"
               "Content-Length: 20\r\n"
               "Content-Type: text/html\r\n"
               "\r\n"
               "Hello\r\n",
    io:format("~p~n", [?LINE]),

    loop( LSock, Responce ).

do_recv( Sock, Bin ) ->
    io:format("~p~n", [?LINE]),
    receive
        { tcp, Sock, Binary } ->
            { ok, [ Binary | Bin ] };
        { tcp_closed, Sock } ->
            { ok, Bin }
    end.

loop( LSock, Responce ) ->
    %% TCPクライアントからの接続要求を受け付ける
    { ok, ASock } = gen_tcp:accept( LSock ),

    io:format("~p~n", [?LINE]),
    %% 受信
    { ok, Bin } = do_recv( ASock, [] ),
    Result = list_to_binary( lists:reverse( Bin ) ),

    io:format("~p~n", [?LINE]),
    %% 送信
    gen_tcp:send( ASock, Responce ),

    io:format("~p~n", [?LINE]),
    %% TCPセッション終了
    ok = gen_tcp:close( ASock ),

    %% 末尾再帰
    loop( LSock, Responce ).


ブラウザから下記のアドレスにアクセスしよう。
http://localhost:12345/



デバッグのために、標準出力でソースの行数が表示されるのは、申し訳ないです。

2012年2月19日日曜日

Erlang TCP接続

通信の勉強中。CでTCPをつかった通信のサンプルコードをサイトを見ながら書いた。

そこで、Cで書いたものと(ほとんど)同じものをErlangで書いた。

動作の概要を以下にイメージ図で示す。

client が一回だけ server に接続して、送られてきたデータ(Hello)を表示するだけのもの。

以下にErlangのソースを示す。

□client

-module( sock_client ).
-compile( export_all ).

client() ->
    %% サーバ接続先
    Server = "localhost",
    %% サーバに接続
    { ok, Sock } = gen_tcp:connect( Server,
                                    12345,
                                    [ binary, { packet, 0} ] ),
    %% サーバからデータ受信
    receive
        { tcp, Sock, Bin } ->
            io:format( "Client get:~p~n", [ binary:bin_to_list( Bin ) ] )
    end,
    %% ソケットの終了
    gen_tcp:close( Sock ).



□server

-module( sock_server ).
-compile( export_all ).

server() ->
    %% TCPクライアントからの接続要求を待てる状態
    { ok, LSock } = gen_tcp:listen( 12345, [ binary, { packet, 0 } ] ),
    %% TCPクライアントからの接続要求を受け付ける
    { ok, ASock } = gen_tcp:accept( LSock ),
    %% 送信
    gen_tcp:send( ASock, "Hello" ),
    %% TCPセッション終了
    ok = gen_tcp:close( ASock ).


server を先に実行しておいてから client を実行する。
そうすると
Client get:"Hello"
と表示されるはず。

参考:



2012年2月18日土曜日

Erlang 2プロセスでフィボナッチ数計算

Erlangで2プロセス(Erlangの軽量プロセスのこと)起動し、それぞれのプロセスがhandler_loop(count_loop)から自然数を取得し、39までのフィボナッチ数を計算する。

1つのプロセスが自然数Nのフィボナッチ数を計算し始めたら、もうひとつのプロセスはN+1を計算します。

なぜこんなことをしたかというと、Cでスレッドおよびmutexロックの勉強をしていて、おなじプログラムを組んだため、それをErlangでやってみました。

Erlangプログラムの概要を図にしました。

□ソース

-module(thread_fib).
-compile(export_all).

-define( _fini, finish ).
-define( _MAX, 40 ).
start() ->
    Pid = self(),
    Pid_count   = spawn( thread_fib, handler_loop, [ Pid ] ),
    Pid1        = spawn( thread_fib, handler_fib, [ Pid, Pid_count, "proc_a" ] ),
    Pid2        = spawn( thread_fib, handler_fib, [ Pid, Pid_count, "proc_b" ] ),
    wait( ).

wait( ) ->
    receive
        ?_fini -> io:format( "start() end." )
    end.

handler_loop( Pid ) ->
    count_loop( Pid, 0, 0 ).

count_loop( StartPid, N, FiniCount ) ->
    receive
        { count, Pid } ->
            Pid ! { count, N },
            count_loop( StartPid, N + 1, FiniCount );
        ?_fini -> case FiniCount < 1 of
                    true  -> count_loop( StartPid, N, FiniCount + 1 );
                    false -> StartPid ! ?_fini
                  end
    end.

handler_fib( Pid1, Pid2, Proc ) ->
    fib_proc( Pid2, Proc ).

fib_proc( Pid, Proc ) ->
    Pid ! { count, self() },
    receive
        { count, N } ->
            case N < ?_MAX of
                true    ->  Fib = fib( N ),
                            io:format( "~p(~p)=(~p)~n", [ Proc, N, Fib ] ),
                            fib_proc( Pid, Proc );
                false   ->  Pid ! ?_fini,
                            io:format( "End ~p~n", [ Proc ] )
            end
    end.


fib( 0 ) -> 1;
fib( 1 ) -> 1;
fib( N ) -> fib( N - 1 ) + fib( N - 2 ).

2012年2月7日火曜日

Erlang AVL Tree まだ途中


コンパイルは通した。
deleteのユニットテストがまだ。
それにしても、ブログに書くと、見難い。

◆ソース

-module( avl_tree ).


-ifdef( debug ).
-compile( export_all ).
-else.
-export( [  lookup/2,
            insert/3,
            update/3,
            delete/2 ] ).
-endif.


-ifdef( debug ).
-include_lib( "eunit/include/eunit.hrl" ).
-define( print(Fmt, Args), io:format ( "~p:~p " Fmt "~n", [ ?MODULE, ?LINE ] ++ Args ) ).
-else.
-define( print(Fmt, Args), ok ).
-endif.




% tree()
-define( undef, undefined ).
-define( bal, balanced ).
%% tree: { tree, key, value, 左部分木, 右部分木, バランス状態 }
%%
%% バランス状態    |        意味
%% ================|==============================================
%% left            | 左部分木が1つだけ高い
%% right           | 右部分木が1つだけ高い
%% balanced        | 左と右の部分木のバランス状態が均衡している


-record( tree, { key=?undef, value=?undef, smaller=?undef, bigger=?undef, dir=?bal } ).
%% @spec delete( Key, tree() ) -> tree() | undefined.




delete( Key, Tree ) when is_record( Tree, tree) ->
    delete_1( Key, Tree );
delete( _, _ ) ->
        undefined.


delete_1( Key, Tree ) when Key < Tree#tree.key ->
        Tree1 = delete_1( Key, Tree#tree.smaller ),
        Tree#tree{ smaller=Tree1 };


delete_1( Key, Tree ) when Key > Tree#tree.key ->
        Tree1 = delete_1( Key, Tree#tree.bigger ),
        Tree#tree{ bigger=Tree1 };


delete_1( _, Tree ) ->
        take_max( Tree ).


%% balance_del( Dir, tree() ) -> { shrinked, tree() } | { unchanged, tree() } | undefined


%% 左部分木にあるノードが削除されると仮定する( Dir = left )
%% A:tree() の頂点ノード
%% B:Aの右側(bigger)のノード
%%              Aの状態                 |
%%      ================================|       木の高さ    |
%%          前      |    後             |       前→後      |   回転
%%      ============|===================|===================|==========
%%      balanced    | right             |  不変             | しない                    --- ①
%%      left        | balanced          |  減る             | しない                    --- ②
%%      right       | BやCの状態による  |  Bの状態による    | する(Bの状態による)       --- ③


%%      Bの状態  |   回転
%%     =====================
%%      right    |   1重            --- ④
%%      left     |   2重            --- ⑤
%%      balanced |   1重            --- ⑥


%% 1重回転                  
%% Bの状態(前)  | Aの状態(後)   Bの状態(後)     高さ
%% =============|====================================
%% right        | right         left            不変            --- ⑦
%% left         | right         left            不変            --- ⑧
%% balanced     | balanced      balanced        減る            --- ⑨


%% 2重回転
%% Cの状態(前)  | Aの状態(後)     Bの状態(後)     Cの状態(後)     高さ
%% =============|========================================================
%% right        | left            balanced        balanced        減る
%% left         | balanced        right           balanced        減る
%% balanced     | balanced        balanced        balanced        減る


%% tree() が定義されていない場合
balance_del( _, undefined ) ->
        undefined;


%% ツリーの頂点がバランス(balanced)状態         --- ①
%% 高さ減らない、木の高さは右が+1
balance_del( left, A ) when A#tree.dir =:= balanced ->
    { unchanged, A#tree{ dir=right } };


%% 高さ減らない、木の高さは左が+1
balance_del( right, A ) when A#tree.dir =:= balanced ->
    { unchanged, A#tree{ dir=left } };


%% ツリーの頂点のバランス状態 = 頂点から見て、ノード削除対象の方向      --- ②
%% 高さが減る(shrinked)、木の高さは左右同じ
balance_del( Dir, A ) when A#tree.dir =:= Dir ->
    { shrinked, A#tree{ dir=balanced } };


%% ツリーの頂点のバランス状態 ≠ 頂点から見て、ノード削除対象の方向     --- ③
balance_del( Dir, A ) ->
    balance_del_B( Dir, A ).


%% balance_del_B( Dir, A ) -> { shrinked, tree() } | { unchanged, tree() }
%% Dir:ノード削除対象の方向
%% A:tree()
balance_del_B( Dir, A ) ->
    %% 
    case Dir =:= left of
        true    -> B = A#tree.smaller;
        false   -> B = A#tree.bigger
    end,
    %% Bのバランス状態とノード削除対象の方向が違う
    case B#tree.dir =/= Dir of
        true    ->  rolling_1( Dir, A );       %% --- ④、⑥
        false   ->  rolling_2( Dir, A )       %% --- ⑤
    end.


%% rolling_1( Dir, tree() ) -> { shrinked, tree() } | { unchanged, tree() }
%% 1重回転
-define( ROLLING_1( X, Y, L, R),
                     ( begin
                        B       = A#tree.X,
                        Alpha   = B#tree.Y,
                        case B#tree.dir of
                            R           -> { unchanged, B#tree{ Y=A#tree{ X=Alpha, dir=R },         dir=L } };
                            L           -> { unchanged, B#tree{ Y=A#tree{ X=Alpha, dir=R },         dir=L } };
                            balanced    -> { shrinked,  B#tree{ Y=A#tree{ X=Alpha, dir=balanced },  dir=balanced } }
                        end
                     end ) ).


rolling_1( left, A ) ->
    ?ROLLING_1( smaller, bigger, left, right );
rolling_1( right, A ) ->
    ?ROLLING_1( bigger, smaller, right, left ).


%% rolling_2
%% 2重回転
-define( rolling_2( X, Y, L, R ),
                 ( begin
                        B = A#tree.X,
                        C = B#tree.Y,
                        Beta1 = C#tree.X,
                        Beta2 = C#tree.Y,
                        case C#tree.dir of
                            R  ->       { shrinked, C#tree{ X=B#tree{ Y=Beta1, dir=balanced },
                                                            Y=A#tree{ X=Beta2, dir=L },
                                                            dir=balanced } };
                            L ->        { shrinked, C#tree{ X=B#tree{ Y=Beta1, dir=R },
                                                            Y=A#tree{ X=Beta2, dir=balanced },
                                                            dir=balanced } };
                            balanced -> { shrinked, C#tree{ X=B#tree{ Y=Beta1, dir=balanced },
                                                            Y=A#tree{ X=Beta2, dir=balanced },
                                                            dir=balanced } }
                        end
                    end 
                )
         ).


rolling_2( left, A ) ->
    ?rolling_2( smaller, bigger, left, right );
rolling_2( right, A ) ->
    ?rolling_2( bigger, smaller, right, left ).


%% @spec take_max( tree() ) -> { Key, Value, tree() } | undefined.
%% Key,Value = tree()内の最大ノード
%% tree() = 取り出した後のツリー
take_max( Tree ) when is_record( Tree, tree ) ->
        take_max_1( Tree );
take_max( _ ) ->
        undefined.


take_max_1( #tree{ key=Key, value=Value, smaller=Smaller, bigger=undefined } )  ->
        { Key, Value, Smaller };
take_max_1( Tree ) ->
        { Key1, Value1, Smaller1 } = take_max_1( Tree#tree.bigger ),
        { Key1, Value1, Tree#tree{ smaller=Smaller1 } }.


%% procCase( X = smaller or bigger, Y = direction = left or right or balanced )
-define( procCase( X, Y ),
        ( begin
               case is_record( SubTree1, tree ) of
                       false->
                                ?print( "tree(false):~p",[ Tree ] ),
                                Tree;
                       true ->
                                Tree1 = Tree#tree{ X=SubTree1 },
                                ?print( "tree(true):~p",[ Tree1 ] ),
                                balance( Y, Tree1 )
               end
          end )
        ).


%% @spec update( Key, Value, Tree ) -> tree()
%% Tree内にKeyが存在する場合、Valueを更新する.
%% Tree内にKeyが存在しない場合、Key/Valueのノードを挿入する.
update( Key, Value, Tree ) ->
        update_1( #tree{ key=Key, value=Value }, Tree ).


update_1( Node, undefined ) ->
        Node;


update_1( Node, Tree ) when Node#tree.key < Tree#tree.key ->
        SubTree1 = update_1( Node, Tree#tree.smaller ),
        ?procCase( smaller, SubTree1 );


update_1( Node, Tree ) when Node#tree.key > Tree#tree.key ->
        SubTree1 = update_1( Node, Tree#tree.bigger ),
        ?procCase( bigger, SubTree1 );


update_1( Node, Tree ) ->
        Tree#tree{ value=Node#tree.value }.


%% @spec insert( Key, Value , Tree ) -> tree() | { key_exists, Value }
%% { key_exists, Value } = already exist.


insert( Key, Value, Tree ) ->
        ?print(" Start.", [] ),
        insert_1( #tree{ key=Key, value=Value }, Tree ).


insert_1( Node, undefined ) ->
        ?print(" Start.", [] ),
        Node;


insert_1( Node, Tree ) when Node#tree.key < Tree#tree.key ->
        ?print(" Start.", [] ),
        SubTree1 = insert_1( Node, Tree#tree.smaller ),
        ?procCase( smaller, left );


insert_1( Node, Tree ) when Node#tree.key > Tree#tree.key ->
        ?print(" Start.", [] ),
        SubTree1 = insert_1( Node, Tree#tree.bigger ),
        ?procCase( bigger, right );


insert_1( _, Tree ) ->
        ?print(" Start.", [] ),
        ?print( "tree:~p",[ { key_exists, Tree#tree.value } ] ),
        { key_exists, Tree#tree.value }.


%% This process use balance/1.
-define( __proc_bal() ,
                        ( begin
                            case Dir =:= SubDir of
                                true ->
                                    ?print( "Dir(true):~p",[ Dir ] ),
                                    balance_1( Tree, Dir );
                                false ->
                                    ?print( "Dir(false):~p",[ Dir ] ),
                                    balance_2( Tree, Dir )
                            end
                        end )
        ).


%% @spec balance( Dir, tree() ) -> tree()
%% Dir : ノードの挿入方向


%% 左部分木に挿入した(挿入方向:left)と仮定(右部分木の場合はleft⇔right)


%% Aの状態(前)  | Aの状態(後)         高さ
%% =============|=================================
%% right        | balanced            不変
%% left         | Bの状態による       Bの状態による
%% balanced     | left                増える


%% Bの状態(前)  | 回転    | Aの状態(後)     | Bの状態(後)
%% =============|=========|=================|=============
%% right        | 2重     | Cの状態による   | Cの状態による
%% left         | 1重     | balanced        | balanced
%% balanced     | 1重     | -               | -


%% Cの状態(前)  | Aの状態(後)     Bの状態(後)     Cの状態(後)
%% =============|===============================================
%% right        | balanced        left            balanced
%% left         | right           balanced        balanced
%% balanced     | balanced        balanced        balanced


%% ノードの挿入方向と木のバランス状態が同じ
balance( Dir, Tree ) when Dir =:= Tree#tree.dir ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    %% 子ノードのバランス状態取得
    case Dir of
        left ->
            SubDir = ( Tree#tree.smaller )#tree.dir,
            ?__proc_bal();
        right ->
            SubDir = ( Tree#tree.bigger )#tree.dir,
            ?__proc_bal();
        _ ->
            unexpect
    end;


%% 木のバランス状態が均衡している
balance( Dir, Tree ) when Tree#tree.dir =:= balanced ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    ?print("Output Tree:(~p).", [ Tree#tree{ dir=Dir } ] ),
    Tree#tree{ dir=Dir };


%% 木のバランス状態と挿入方向が違う
balance( Dir, Tree ) ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    Tree#tree{ dir=balanced }.


%% 1回転させる処理
%% ( X, Y ) = ( smaller, bigger ) or ( bigger, smaller )
-define( __proc_bal_1( X, Y ),
                        ( begin
                            ?print("Input ( X, Y ) = ( ~p, ~p ).", [ X, Y ] ),
                            Sub = Tree#tree.X,
                            SubBig = Sub#tree.Y,
                            Bigger = Tree#tree{ X=SubBig, dir=balanced },
                            ?print("Output:( ~p ) .", [ Sub#tree{ Y=Bigger, dir=balanced } ] ),
                            Sub#tree{ Y=Bigger, dir=balanced }
                        end )
        ).
%% 1回転(left)
%% Tree:回転させる木
%% left:Treeからのノードの挿入方向
balance_1( Tree, left ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_1( smaller, bigger );


%% 1回転(right)
%% Tree:回転させる木
%% right:Treeからのノードの挿入方向
balance_1( Tree, right ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_1( bigger, smaller ).


%% 2回転させる処理
%% ( X, Y ) = ( smaller, bigger ) or ( bigger, smaller )
%% Z = Treeからのノード挿入方向( left or right )
-define( __proc_bal_2( X, Y, Z ),
                                ( begin
                                    ?print("Input ( X, Y, Z ) = ( ~p, ~p, ~p ).", [ X, Y, Z ] ),
                                    Sub = Tree#tree.X,
                                    SubBig = Sub#tree.Y,
                                    case Tree#tree.dir of
                                        balanced ->
                                            ?print( "Output(balanced): (~p) ", [SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z }, 
                                                    dir=balanced } ] ),
                                            SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=balanced }, 
                                                    dir=Z };
                                        _ ->
                                            ?print( "Output(~p): (~p) ", [ Tree#tree.dir, SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z }, 
                                                    dir=balanced } ] ),
                                            SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced }, 
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z }, 
                                                    dir=balanced }
                                    end
                                end )
        ).
%% 2回転
%% @spec balance_2( tree(), Direction ) -> tree()
%% Direction : tree()からのノード挿入方向
balance_2( Tree, left ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_2( smaller, bigger, right );


balance_2( Tree, right ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_2( bigger, smaller, left ).






%% @spec lookup( Target, Tree ) -> undefined | { value, Value }
lookup ( Key, T ) ->
        lookup_1 ( Key, T ).


lookup_1 ( _, undefined ) ->
        ?print( "", [] ),
        undefined;


lookup_1 ( Key, Tree ) when Key < Tree#tree.key ->
        ?print( "", [] ),
        lookup_1 ( Key, Tree#tree.smaller );


lookup_1 ( Key, Tree ) when Tree#tree.key < Key ->
        ?print( "", [] ),
        lookup_1 ( Key, Tree#tree.bigger );


lookup_1 ( _, Tree ) ->
        ?print( "", [] ),
        { value, Tree#tree.value }.

2012年2月4日土曜日

Erlang avl tree insert を作った

今日は、AVL tree の挿入を。
アルゴリズムは、各自Wikipedia等でお願いします。

わかりづらいソースなので、コメントを厚くしたものを載せるつもりです。

◆ソース

-module( avl_tree_insert ).

-ifdef( debug ).
-compile( export_all ).
-else.
-export( [ insert/3 ] ).
-endif.

-ifdef( debug ).
-include_lib( "eunit/include/eunit.hrl" ).
-define( print(Fmt, Args), io:format ( "~p:~p " Fmt "~n", [ ?MODULE, ?LINE ] ++ Args ) ).
-else.
-define( print(Fmt, Args), ok ).
-endif.


% tree()
-define( undef, undefined ).
-define( bal, balanced ).
%% tree: { tree, key, value, 左部分木, 右部分木, バランス状態 }

%% バランス状態    |        意味
%% ============|==============================================
%% left                      | 左部分木が1つだけ高い
%% right                    | 右部分木が1つだけ高い
%% balanced             | 左と右の部分木のバランス状態が均衡している


-record( tree, { key=?undef, value=?undef, smaller=?undef, bigger=?undef, dir=?bal } ).
-define( ArgNode, { Key, _ } = Node ).
-define( ArgTree, #tree{ key=Key1, value=Value, smaller=Smaller, bigger=Bigger } = Tree ).
-define( procInsert( X, Y, Z), X = insert_1( Y, Z ) ).


%% procCase( X = smaller | bigger, Y = direction = left or right or balanced )
-define( procCase( X, Y ),
        ( begin
               case is_record( SubTree1, tree ) of
                       false->
                                ?print( "tree(false):~p",[ Tree ] ),
                                Tree;
                       true ->
                                Tree1 = Tree#tree{ X=SubTree1 },
                                ?print( "tree(true):~p",[ Tree1 ] ),
                                balance( Y, Tree1 )
               end
          end )
        ).

%% @spec insert( Key, Value , Tree ) -> tree() | { key_exists, Value }
%% { key_exists, Value } = already exist.

insert( Key, Value, Tree ) ->
        ?print(" Start.", [] ),
        insert_1( #tree{ key=Key, value=Value }, Tree ).

insert_1( Node, undefined ) ->
        ?print(" Start.", [] ),
        Node;

insert_1( Node, Tree ) when Node#tree.key < Tree#tree.key ->
        ?print(" Start.", [] ),
        SubTree1 = insert_1( Node, Tree#tree.smaller ),
        ?procCase( smaller, left );

insert_1( Node, Tree ) when Node#tree.key > Tree#tree.key ->
        ?print(" Start.", [] ),
        SubTree1 = insert_1( Node, Tree#tree.bigger ),
        ?procCase( bigger, right );

insert_1( _, Tree ) ->
        ?print(" Start.", [] ),
        ?print( "tree:~p",[ { key_exists, Tree#tree.value } ] ),
        { key_exists, Tree#tree.value }.

%% This process use balance/1.
-define( __proc_bal() ,
                        ( begin
                            case Dir =:= SubDir of
                                true ->
                                    ?print( "Dir(true):~p",[ Dir ] ),
                                    balance_1( Tree, Dir );
                                false ->
                                    ?print( "Dir(false):~p",[ Dir ] ),
                                    balance_2( Tree, Dir )
                            end
                        end )
        ).

%% @spec balance( Dir, tree() ) -> tree()
%% Dir : ノードの挿入方向

%% @spec balance( Dir, tree() ) -> tree()
%% Dir : ノードの挿入方向

%% 左部分木に挿入した(挿入方向:left)と仮定(右部分木の場合はleft⇔right)

%% Aの状態(前)  | Aの状態(後)         高さ
%% ========== |=================================
%% right                 | balanced                不変
%% left                   | Bの状態による    Bの状態による
%% balanced          | left                         増える

%% Bの状態(前)  | 回転    | Aの状態(後)     | Bの状態(後)
%% ========== |===== |============ |=============
%% right                 | 2重     | Cの状態による  | Cの状態による
%% left                   | 1重     | balanced              | balanced
%% balanced          | 1重     | -                          | -

%% Cの状態(前)  | Aの状態(後)     Bの状態(後)     Cの状態(後)
%% ==========|=============================================
%% right                | balanced             left                      balanced
%% left                  | right                    balanced             balanced
%% balanced         | balanced             balanced             balanced


%% ノードの挿入方向と木のバランス状態が同じ
balance( Dir, Tree ) when Dir =:= Tree#tree.dir ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    %% 子ノードのバランス状態取得
    case Dir of
        left ->
            SubDir = ( Tree#tree.smaller )#tree.dir,
            ?__proc_bal();
        right ->
            SubDir = ( Tree#tree.bigger )#tree.dir,
            ?__proc_bal();
        _ ->
            unexpect
    end;

%% 木のバランス状態が均衡している
balance( Dir, Tree ) when Tree#tree.dir =:= balanced ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    ?print("Output Tree:(~p).", [ Tree#tree{ dir=Dir } ] ),
    Tree#tree{ dir=Dir };

%% 木のバランス状態と挿入方向が違う
balance( Dir, Tree ) ->
    ?print(" Start.", [] ),
    ?print("Input Dir:(~p) Tree:(~p).", [ Dir, Tree ] ),
    Tree#tree{ dir=balanced }.

%% 1回転させる処理
%% ( X, Y ) = ( smaller, bigger ) or ( bigger, smaller )
-define( __proc_bal_1( X, Y ),
                        ( begin
                            ?print("Input ( X, Y ) = ( ~p, ~p ).", [ X, Y ] ),
                            Sub = Tree#tree.X,
                            SubBig = Sub#tree.Y,
                            Bigger = Tree#tree{ X=SubBig, dir=balanced },
                            ?print("Output:( ~p ) .", [ Sub#tree{ Y=Bigger, dir=balanced } ] ),
                            Sub#tree{ Y=Bigger, dir=balanced }
                        end )
        ).
%% 1回転(left)
%% Tree:回転させる木
%% left:Treeからのノードの挿入方向
balance_1( Tree, left ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_1( smaller, bigger );

%% 1回転(right)
%% Tree:回転させる木
%% right:Treeからのノードの挿入方向
balance_1( Tree, right ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_1( bigger, smaller ).

%% 2回転させる処理
%% ( X, Y ) = ( smaller, bigger ) or ( bigger, smaller )
%% Z = Treeからのノード挿入方向( left or right )
-define( __proc_bal_2( X, Y, Z ),
                                ( begin
                                    ?print("Input ( X, Y, Z ) = ( ~p, ~p, ~p ).", [ X, Y, Z ] ),
                                    Sub = Tree#tree.X,
                                    SubBig = Sub#tree.Y,
                                    case Tree#tree.dir of
                                        balanced ->
                                            ?print( "Output(balanced): (~p) ", [SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced },
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z },
                                                    dir=balanced } ] ),
                                            SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y },
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=balanced },
                                                    dir=Z };
                                        _ ->
                                            ?print( "Output(~p): (~p) ", [ Tree#tree.dir, SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced },
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z },
                                                    dir=balanced } ] ),
                                            SubBig#tree{
                                                    X=Sub#tree{ Y=SubBig#tree.Y, dir=balanced },
                                                    Y=Tree#tree{ X=SubBig#tree.Y, dir=Z },
                                                    dir=balanced }
                                    end
                                end )
        ).
%% 2回転
%% @spec balance_2( tree(), Direction ) -> tree()
%% Direction : tree()からのノード挿入方向
balance_2( Tree, left ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_2( smaller, bigger, right );

balance_2( Tree, right ) ->
    ?print(" Start.", [] ),
    ?__proc_bal_2( bigger, smaller, left ).



ソースとテストソースとmakeファイル(omake)は、以下のリンクから
https://docs.google.com/open?id=0B8Fi1kuQJgFrMDgyMTFiMzQtMDc1OS00Mzk5LTlhY2UtYmM2NGY2N2Q0MGQw

参考:
Amazon.co.jp: アルゴリズムとデータ構造 (岩波講座 ソフトウェア科学 3): 石畑 清: 本 : http://goo.gl/B6gKx


2012年1月25日水曜日

Erlang 2分探索木 take_smallest 最小ノード取り出し

今日は、2分探索木の削除の一部を。

2分探索木のツリーからあるノードを削除する場合、削除対象ノードの右の部分木から最小のノードを取り出し、削除対象ノードと置き換える動作を行う。

削除の中核をなすこの動作のソースを記載したいと思う。

◆take_smallest/1
  ツリーから最小ノードを取り出し、取り出した後のツリーとのタプルを返す。

【構文】
take_smallest( tree() ) -> { Key, Value, tree() }
(Key, Value:右の部分木の最小ノードのキーバリュー)
(tree():最小ノードを取り出した後のツリー)

【ソース】

-module( binary_tree_take_smallest ).
-export( [ take_smallest/1 ] ).

-ifdef( debug ).
-include_lib( "eunit/include/eunit.hrl" ).
-endif.

-ifdef( debug ).
-define( T1, #tree{ key=nine, value=9 } ).
-define( Big, #tree{ key=ten, value=10 } ).
-define( Sml, #tree{ key=eight, value=8 } ).
-define( T2, #tree{ key=nine, value=9, bigger=?Big } ).
-define( T2L, #tree{ key=nine, value=9, smaller=?Sml } ).
%-define( T1, #tree{ ker=nine, 9, undefined, undefined } ).
-define( Tree, #tree{ key=nine, value=9, smaller=#tree{ key=five, value=5 }, bigger=?Big } ).
-endif.

% tree()
-record( tree, { key, value, smaller, bigger } ).

%% @spec take_smallest( tree() ) -> { Key, Value, tree() }.
take_smallest( Tree ) when is_record( Tree, tree ) ->
        take_smallest_1( Tree ).

take_smallest_1( #tree{ key=Key, value=Value, smaller=undefined, bigger=Bigger } )  ->
        { Key, Value, Bigger };
take_smallest_1( Tree ) ->
        { Key1, Value1, Smaller1 } = take_smallest_1( Tree#tree.smaller ),
        { Key1, Value1, Tree#tree{ smaller=Smaller1 } }.

-ifdef(debug).
t1_test() -> ?assert( { nine, 9 , undefined } =:= take_smallest_1( ?T1 ) ).
t2_test() -> ?assert( { nine, 9, ?Big } =:= take_smallest_1( ?T2 ) ).
t3_test() -> ?assert( { eight, 8, ?T1 } =:= take_smallest_1( ?T2L ) ).
t4_test() -> ?assert( { five, 5, ?T2 } =:= take_smallest_1( ?Tree ) ).
-endif.

参考:
gb_tree.erlのtake_smallest/1

2012年1月22日日曜日

Erlang 2分探索木 更新 update

今日は、2分探索木の更新(update/3)を。青文字部分はユニットテストのためのソース。

仕様は以下の2点。
1.更新するノード(Key)がある場合、Valueを更新する。
2.更新するノード(Key)がない場合、対象ノードを挿入する。

◆ソース

-module( binary_tree_update ).
-export( [ update/3 ] ).

-ifdef( debug ).
-include_lib( "eunit/include/eunit.hrl" ).
-define( print(Fmt, Args), io:format ( "~p:~p " Fmt "~n", [ ?MODULE, ?LINE ] ++ Args ) ).
-else.
-define( print(Fmt, Args), ok ).
-endif.

-ifdef( debug ).
-define( E, #tree{ key=nine, value=9 } ).
-define( T1, #tree{ key=nine, value=9 } ).
%-define( T1, #tree{ ker=nine, 9, undefined, undefined } ).
-define( Tree, #tree{ key=nine, value=9, smaller=#tree{ key=five, value=5 }, bigger=#tree{ key=ten, value=10 } } ).
-endif.

% tree()
-record( tree, { key, value, smaller, bigger } ).
-define( procCase( X, Y ),
        ( begin
               case is_record( SubTree1, tree ) of
                       false->
                               ?print( "",[] ),
                               Tree;
                       true ->
                               ?print( "",[] ),
                               Tree#tree{ X=Y }
               end
          end )
        ).
-define( ArgNode, { Key, _ } = Node ).
-define( ArgTree, #tree{ key=Key1, value=Value, smaller=Smaller, bigger=Bigger } = Tree ).
-define( procInsert( X, Y, Z), X = insert_1( Y, Z ) ).

%% @spec update( Key, Value, Tree ) -> tree()
%% Tree内にKeyが存在する場合、Valueを更新する.
%% Tree内にKeyが存在しない場合、Key/Valueのノードを挿入する.
update( Key, Value, Tree ) ->
        update_1( #tree{ key=Key, value=Value }, Tree ).

update_1( Node, undefined ) ->
        Node;

update_1( Node, Tree ) when Node#tree.key < Tree#tree.key ->
        SubTree1 = update_1( Node, Tree#tree.smaller ),
        ?procCase( smaller, SubTree1 );

update_1( Node, Tree ) when Node#tree.key > Tree#tree.key ->
        SubTree1 = update_1( Node, Tree#tree.bigger ),
        ?procCase( bigger, SubTree1 );

update_1( Node, Tree ) ->
        Tree#tree{ value=Node#tree.value }.

-ifdef(debug).
%% insert test
u1_test() -> 
        #tree{ key=nine, value=9 } 
        = 
        update_1( ?E, undefined ).
u3_test() -> 
        #tree{ key=nine, value=9, smaller=#tree{ key=eight, value=8 } } 
        = 
        update_1( #tree{ key=eight, value=8 }, ?T1 ).
u4_test() -> 
        #tree{ key=nine, value=9, bigger=#tree{ key=ten, value=10 } } 
        = 
        update_1( #tree{ key=ten, value=10 }, ?T1 ).
u5_test() ->
        #tree{ key=nine,
               value=9,
               smaller=
                    #tree{ key=five, 
                           value=5,
                           smaller=#tree{ key=eight, value=8 } },
               bigger=#tree{ key=ten, value=10 } } 
        =
        update_1( #tree{ key=eight, value=8 }, ?Tree ).
%% update test
u6_test() ->?assert(
        #tree{ key=nine, value=90 }
       =:=
        update_1( #tree{ key=nine, value=90 }, ?T1 )
        ).
u2_test() -> ?assert(
        ?Tree 
        =:=
        update_1( ?E, ?Tree ) ).
u7_test() -> ?assert(
        #tree { key=nine, value=9, smaller=#tree{ key=five, value=50 }, bigger=#tree{ key=ten, value=10 } }
        =:=
        update_1( #tree{ key=five, value=50 }, ?Tree ) ).
u8_test() -> ?assert(
        #tree { key=nine, value=9, smaller=#tree{ key=five, value=5 }, bigger=#tree{ key=ten, value=100 } }
        =:=
        update_1( #tree{ key=ten, value=100 }, ?Tree ) ).
-endif.

Erlang 2分探索木 挿入 insert

今日は、2分探索木のinsertを作ってみた。
■insert/2
insert( Key, Value , Tree ) -> tree() | {key_exists, Value }

{key_exists, Value }はすでに同一キーがTreeにある場合。
tree()は挿入後のツリー


【ソース】

-module( binary_tree_insert ).
-export( [ insert/3 ] ).

-ifdef( debug ).
-include_lib( "eunit/include/eunit.hrl" ).
-define( print(Fmt, Args), io:format ( "~p:~p " Fmt "~n", [ ?MODULE, ?LINE ] ++ Args ) ).
-else.
-define( print(Fmt, Args), ok ).
-endif.

% tree()
-record( tree, { key, value, smaller, bigger } ).
-define( procCase( X, Y ),
        ( begin
               case is_record( SubTree1, tree ) of
                       false->
                               ?print( "",[] ),
                               Tree;
                       true ->
                               ?print( "",[] ),
                               Tree#tree{ X=Y }
               end
          end )
        ).
-define( ArgNode, { Key, _ } = Node ).
-define( ArgTree, #tree{ key=Key1, value=Value, smaller=Smaller, bigger=Bigger } = Tree ).
-define( procInsert( X, Y, Z), X = insert_1( Y, Z ) ).

%% @spec insert( Key, Value , Tree ) -> tree() | {key_exists, Value }
%% { key_exists, Value } = already exist.

insert( Key, Value, Tree ) ->
        insert_1( #tree{ key=Key, value=Value }, Tree ).

insert_1( Node, undefined ) ->
        Node;

insert_1( Node, Tree ) when Node#tree.key < Tree#tree.key ->
        SubTree1 = insert_1( Node, Tree#tree.smaller ),
        ?procCase( smaller, SubTree1 );

insert_1( Node, Tree ) when Node#tree.key > Tree#tree.key ->
        SubTree1 = insert_1( Node, Tree#tree.bigger ),
        ?procCase( bigger, SubTree1 );

insert_1( _, Tree ) ->
        { key_exists, Tree#tree.value }.

-ifdef(debug).
-define( E, #tree{ key=nine, value=9 } ).
-define( T1, #tree{ key=nine, value=9 } ).
%-define( T1, #tree{ ker=nine, 9, undefined, undefined } ).
-define( Tree, #tree{ key=nine, value=9, smaller=#tree{ key=five, value=5 }, bigger=#tree{ key=ten, value=10 } } ).

i1_test() ->
        #tree{ key=nine, value=9 }
        =
        insert_1( ?E, undefined ).
i2_test() ->
        { key_exists, 9 }
        =
        insert_1( ?E, ?Tree ).
i3_test() ->
        #tree{ key=nine, value=9, smaller=#tree{ key=eight, value=8 } }
        =
        insert_1( #tree{ key=eight, value=8 }, ?T1 ).
i4_test() ->
        #tree{ key=nine, value=9, bigger=#tree{ key=ten, value=10 } }
        =
        insert_1( #tree{ key=ten, value=10 }, ?T1 ).
i5_test() ->
        #tree{ key=nine,
               value=9,
               smaller=
                    #tree{ key=five,
                           value=5,
                           smaller=#tree{ key=eight, value=8 } },
               bigger=#tree{ key=ten, value=10 } }
        =
        insert_1( #tree{ key=eight, value=8 }, ?Tree ).
-endif.


参考:
M.Hiroi's Home Page / お気楽 Erlang プログラミング入門 : http://www.geocities.jp/m_hiroi/func/abcerl05.html
Erlang -- erlang : http://www.erlang.org/doc/man/erlang.html#is_record-2
letter: 2007/05 - 2007/06 : http://read-eval-print.blogspot.com/2007_05_01_archive.html
gb_tree.erlのソース

2012年1月21日土曜日

Erlang io:format デバッグ 可変引数

Erlangには、可変引数がないみたい。検索に引っかからない。
どうにか、デバッグ(io:format)で可変引数っぽくできないかと考え、作ってみた。

■ソース

-module(va_args).
-export([start/0]).
-define( LOG( Fmt, Args ), io:format( Fmt, Args ) ).
-define( LOG2( Fmt, Args ), io:format( "~p:~p " Fmt, [ ?MODULE, ?LINE ] ++ Args ) ).

start() ->
        ?LOG( "~p~n", [test] ),
        ?LOG2( "~p~n", [test2] ).

■実行結果

C:\Users\andre\ework>erlc va_args.erl

C:\Users\andre\ework>erl -noshell -run va_args start -run init stop
test
va_args:8 test2


引数にフォーマットとリスト渡せば、可変引数として使える。

なぜ、デバッグでio:format?と思われるかもしれないが、まだ技術力が足りないせいである。


Erlang 2分探索木 探索 lookup

今日は、2分探索木の探索についてのソースを。
【データ構造】
-record( tree, { key, value, smaller, bigger } ).
タプルだね。

【アルゴリズム】
基本的な2分探索木のサーチ

■ソース(gb_tree.erlを参考に作りました:ほぼ一緒)


-module( bin_tree ).
-export( [  lookup/2 ] ).

-ifdef( debug ).
-include_lib( "eunit/include/eunit.hrl" ).
-define( print(Fmt, Args), io:format ( "~p:~p " Fmt "~n", [ ?MODULE, ?LINE ] ++ Args ) ).
-else.
-define( print(Fmt, Args), ok ).
-endif.

% tree()
-record( tree, { key, value, smaller, bigger } ).
-define( Tree, #tree{ key=nine, value=9, smaller=#tree{ key=five, value=5 }, bigger=#tree{ key=ten, value=10 } } ).

%% @spec lookup( Target, Tree ) -> undefined | { value, Value }
lookup ( Key, T ) ->
        lookup_1 ( Key, T ).

lookup_1 ( _, undefined ) ->
        ?print( "", [] ),
        undefined;

lookup_1 ( Key, Tree ) when Key < Tree#tree.key ->
        ?print( "", [] ),
        lookup_1 ( Key, Tree#tree.smaller );

lookup_1 ( Key, Tree ) when Tree#tree.key < Key ->
        ?print( "", [] ),
        lookup_1 ( Key, Tree#tree.bigger );

lookup_1 ( _, Tree ) ->
        ?print( "", [] ),
        { value, Tree#tree.value }.

-ifdef(debug).
%-define( Tree, { nine, 9, { five, 5, undefined, undefined }, {ten, 10, undefined, undefined} } ).
-define( L(X,Y), lookup (X, Y) ).
l1_test() -> { value, 9 } = ?L( nine, ?Tree ).
l2_test() -> { value, 5 } = ?L( five, ?Tree ).
l3_test() -> { value, 10 } = ?L( ten, ?Tree ).
l4_test() -> undefined = ?L( eight, ?Tree ).
-endif.



2012年1月17日火曜日

Erlang queueモジュールによるstackモジュール作成

stackモジュールがないので、作ってみた。突貫で。
コンパイルとおしただけ。

1番下にある追記参照。

【ソース】


-module(stack).
-ifdef(debug).
-include_lib("eunit/include/eunit.hrl").
-endif.
-export([new/0,
        is_stack/1,
        is_empty/1,
        len/1,
        push/2,
        pop/1,
        from_list/1,
        to_list/1,
        reverse/1
        ]).
% -----
% stack of queue module
% -----
% stack()
-record( stack, { stk=undefined } ).
-define( __q, queue ).
-define( __is_q( X ), ?__q:is_queue( X ) ).
-define( __proc_q( X ), ( begin
                            case ?__is_q( Stack ) of
                                true ->
                                    ?__q:X( Stack );
                                _ ->
                                    error
                            end
                        end )
       ).
% @spec new() -> stack()
new() ->
    #stack{ stk=queue:new() }.

% @spec is_stack(Term::term()) -> boolean
is_stack( #stack{ stk=Stack } ) ->
    ?__is_q( Stack );
is_stack( _ ) ->
    error.

% @spec is_empty(S :: stack()) -> boolean | error
%is_empty(Stack) ->
is_empty( #stack{ stk=Stack } ) ->
    ?__proc_q( is_empty );

is_empty( _ ) ->
    error.

% @spec len( stack() ) -> integer() >= 0 | error
len( #stack{ stk=Stack } ) ->
    ?__proc_q( len );
len( _ ) ->
    error.

% @spec push( term(), stack() ) -> stack() | error
push( Item, #stack{ stk=Stack } ) ->
    case ?__is_q( Stack ) of
        true ->
            Stack1 = queue:in( Item, Stack ),
            #stack{ stk=Stack1 };
        _ ->
            error
    end;
push( _, _ ) ->
    error.

% @spec pop( stack() ) -> {{value, term()}, stack()} | {empty, stack()} | error
pop( #stack{ stk=Stack } ) ->
    Stack1 = ?__proc_q( out_r ),
    case Stack1 of
        { { value, Value }, Queue } ->
            { {value, Value}, #stack{ stk=Queue } };
        { empty, Queue } ->
            { empty, #stack{ stk=Queue } };
        _ ->
            error
    end;
pop( _ ) ->
    error.

% @spec from_list( list() ) -> stack()
from_list( List ) when is_list( List ) ->
        Stack = queue:from_list( List ),
        #stack{ stk=Stack };
from_list( _ ) ->
        error.

% @spec to_list( stack() ) -> list()
to_list( #stack{ stk=Stack } ) ->
    case ?__is_q( Stack ) of
        true ->
            queue:to_list( Stack );
        _ ->
            error
    end;
to_list( _ ) ->
    error.

%@spec reverse( stack() ) -> stack() | error
reverse( #stack{ stk=Stack } ) ->
    Stack1 = ?__proc_q( reverse ),
    case Stack1 of
        error ->
            error;
        _ ->
            { stack, Stack1 }
    end;
reverse( _ ) ->
    error.




Erlang 2分探索 速報版

サンプル(速報版)

-module(binary_search).
-export([search/2]).
-include_lib("eunit/include/eunit.hrl").
-define(Middle,Mid = (Lo + Hi) div 2).
-ifdef(debug).
-define(print,io:format("~p:~p~n",[?MODULE,?LINE])).
-else.
-define(print,ok).
-endif.

% @spec search(Array,Target) -> {true,pos} | false
search(Array,Target) ->
        i_search(Array,Target,0,array:size(Array)-1).

i_search(Array,Target,Lo,Hi) when Lo =< Hi ->
    ?Middle,
    X = array:get(Mid,Array),
    if
           Target < X ->
                   ?print,
                   i_search(Array,Target,Lo,Mid -1);
           true ->
                  ?print,
                  i_search(Array,Target,Mid + 1, Hi)
   end;

i_search(Array,Target,Lo,Hi) when Lo > Hi ->
    Mid = (Lo + Hi ) div 2,
    X = array:get(Mid,Array),
    if
            Target =:= X ->
                    ?print,
                    {true,Hi};
            Hi =:= 0 ->
                    ?print,
                    false;
            true ->
                    ?print,
                    false
    end.

-ifdef(debug).
-define(FUNC(X,Y),search(X,Y)).
-define(AL(),array:from_list( [1,3,4,6,89] )).
s1_test() -> {true,0} = ?FUNC(?AL(),1).
s2_test() -> {true,1} = ?FUNC(?AL(),3).
s3_test() -> {true,2} = ?FUNC(?AL(),4).
s4_test() -> {true,3} = ?FUNC(?AL(),5).
s5_test() -> false = ?FUNC(?AL(),6).
-endif.

果たして、これでいいのか。