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