2012年10月28日日曜日

[Erlang] URLにある内容を読み込む。

はじめに

本記事は、http://www.ruby-lang.org/の内容(html)を読み込んで、その内容のデータサイズ(文字列長)を表示するプログラムをErlangで書いたものである。

本当は、htmlをパースして、タイトルを抜き出したかったが、Erlangでどうやればいいのかわからず、断念した。

プログラム処理概要

  1. URLの内容を読み込む
  2. 内容のデータサイズを表示する。

ソース

-module(test_httpc).
-export([start/0]).
start() ->
    inets:start(),
    { ok, { { Version, 200, ReasonPhrase }, Headers, Body } }
        = httpc:request( get, { "http://www.ruby-lang.org/", [] }, [], []),
    Length = string:len( Body ),
    io:format("length:~w~n", [Length] ).

実行

andre@andre-VirtualBox:~/work/erlang$ erl
Erlang R14B02 (erts-5.8.3) [source] [rq:1] [async-threads:0] [kernel-poll:false]
Eshell V5.8.3  (abort with ^G)
1> c(test_httpc).
./test_httpc.erl:6: Warning: variable 'Headers' is unused
./test_httpc.erl:6: Warning: variable 'ReasonPhrase' is unused
./test_httpc.erl:6: Warning: variable 'Version' is unused
{ok,test_httpc}
2> test_httpc:start().
length:11613
ok
3> 

2012年9月30日日曜日

Electric Fence インストール方法

ただのメモです。
Electric Fenceのインストール方法

$ sudo apt-get install electric-fence

2012年5月30日水曜日

cmake 静的ライブラリを作り、実行ファイルに取り込んでみた

cmake を使って、静的ライブラリを作ってみました。 そして、それを実行ファイルに取り込んでみた。

そのサンプルを下記に置いておきます。

tomohikoseven/libstatic : https://github.com/tomohikoseven/libstatic

2012年5月19日土曜日

ruby rb_exc_fatal を試した

サンプルコードを置いておきます。
GitHub:
https://github.com/tomohikoseven/rb_exc_fatal

メインスレッドとサブスレッド2枚動作中、サブスレッドがfatalの例外を投げて、それをメインスレッドが捕捉します。
サブスレッドもrescueしてるけど、捕捉しない。

2012年5月14日月曜日

minix 3.0 ソースコード

固めてみた。
git じゃないと取得できないので。
権利関係で違反してたら、Google Docsでの共有止めちゃう。

minix 3.0
https://docs.google.com/open?id=0B8Fi1kuQJgFrenZBYlYwT2lCaFU

2012年4月30日月曜日

omake Stack_overflow と怒られる

omake で幸せになろうと頑張っている最中に、

Fatal error: exception Stack_overflow

と怒られたので、そのときの対処を備忘録として。

結論からいうと、今作っているものはカレントディレクトリにのみファイルがあるため、

OMakefile の .SUBDIRS をコメントアウトする

という対処を行った。

ここが「.SUBDIRS = . 」となっていたため、無限ループみたいになって、スタックが足りなくなり、オーバーフローを起こしていたのだ。(おそらく)

その他詳細は、何も調べていないので、書けない。

2012年4月29日日曜日

ruby 拡張ライブラリ rb_thread_create サンプル

rb_thread_create のサンプル書いてみた。

下記コマンド実行で動くはず。

$ ruby extconf.rb
$ make
$ ruby fib.rb

test1.c と fib.c が拡張ライブラリで、fib.rb が拡張ライブラリ利用者。

(fib.rb)
 $LOAD_PATH << "."
 require "test"
 include Test

 Test.thread
(fib.c)
#include <stdio.h>
int fib( int n )
{
    if( n < 0 ) return -1;
    if( n == 0 ) return 1;
    if( n == 1 ) return 1;
    if( n > 1 ) return fib( n-1 ) + fib( n-2 );
}
(test1.c)
#include "ruby.h"

int fib();

int gloval = 0;
VALUE wrap_fib( VALUE self )
{
    int n;
    int count = 0;
    for( count = 0; count < 30; count++ )
    {
        n = gloval;
        printf( "fib(%d):%d\n", n, fib(n) );
        gloval = (n + 1) % 30;
    }
    return Qnil;
}
VALUE wrap_thread( VALUE self )
{
    rb_thread_create( wrap_fib, NULL);
    return Qnil;
}
void Init_test()
{
    VALUE module;
    module = rb_define_module( "Test" );
    rb_define_module_function( module, "thread", wrap_thread, 0 );
}
(extconf.rb)
require 'mkmf'
create_makefile( "test" )

ruby rvm 拡張ライブラリ 公開ヘッダ ruby.h 場所

拡張ライブラリを作っていろいろ作業をしている。
直接公開ヘッダファイルを見たいけど、どこにあるのかと探した結果を下記に示す。

andre@andre-VirtualBox:/usr$ rvm -v

rvm 1.13.0 () by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/]

andre@andre-VirtualBox:/usr$ sudo find / -name ruby.h
[sudo] password for andre:
/usr/lib/ruby/1.8/i686-linux/ruby.h
/var/cache/ruby-rvm/rubies/ruby-1.9.3-p194/include/ruby-1.9.1/ruby/ruby.h
/var/cache/ruby-rvm/rubies/ruby-1.9.3-p194/include/ruby-1.9.1/ruby.h
/var/cache/ruby-rvm/rubies/ruby-1.9.3-p125/include/ruby-1.9.1/ruby/ruby.h
/var/cache/ruby-rvm/rubies/ruby-1.9.3-p125/include/ruby-1.9.1/ruby.h
/var/cache/ruby-rvm/src/ruby-1.9.3-p194/include/ruby/ruby.h
/var/cache/ruby-rvm/src/ruby-1.9.3-p194/include/ruby.h
/var/cache/ruby-rvm/src/ruby-1.9.3-p125/include/ruby/ruby.h
/var/cache/ruby-rvm/src/ruby-1.9.3-p125/include/ruby.h

ruby1.9 簡単な拡張ライブラリ 作成時 注意

簡単な拡張ライブラリを作成したとき、
カレントディレクトリに拡張ライブラリと
それを利用するテストrubyを作成すると思う。

ruby1.9になると、テストrubyは拡張ライブラリを取り込むことに失敗することがある。
それは、

カレントディレクトリがLOAD_PATHとして設定されていないからだ。
その一つの対処として、テストrubyに下記を追記する。

(テストruby)
$LOAD_PATH << "."

require 'xx"

2012年4月28日土曜日

ubuntu 11.10 google 日本語入力システム iBus-Mozc インストール

1.下記コマンドを実行する。

$ sudo apt-get install ibus-Mozc

2.ログアウトする。

3.端末(terminal)を起動し、下記コマンドを実行する。

$ ibus-setup

4.表示された画面の「インプットメソッド」タブを選択する。

5.「インプットメソッドの選択」プルダウンから、「日本語」ー「Mozc」を選択する。

6.「インプットメソッド」欄の「Mozc」を選択し、「上へ(U)」ボタンを押下し、最上段へ移動させる。

スクリーンショットがなくて申し訳ない。

VirtualBox ネットできない 原因 共有フォルダ

VirtualBox 4.1.10 上で ubuntu 11.10 を動作させていた。
VirtualBox の共有フォルダの設定をしたら、ubuntu 起動後、ネットができなくなった。
かなしい。

共有フォルダ以外の設定で変更したのは、CPUの数をホストと同じ2に設定しただけ。

2012年4月15日日曜日

ruby rvm version 切り替えできない 解決法

rubyの勉強中。

ruby 1.9.xの環境がほしくて、インストールしようとしたら、ruby 1.8.7 の環境と共存できるツール rvm あると知って、インストールした。

インストール参考:

複数のRuby環境を共存させられるRVM(Ruby Version Manager)を使う - アインシュタインの電話番号☎ : http://d.hatena.ne.jp/ruedap/20110112/ruby_version_manager_rvm_install

rvm を使って versionの切り替えをしようとしたらできなかった。

原因は、bashrcに追記する設定でした。

上記サイト:

if [[ -s ~/.rvm/scripts/rvm ]] ; then source ~/.rvm/scripts/rvm ; fi

修正

if [[ -s /usr/local/rvm/scripts/rvm ]] ; then source /usr/local/rvm/scripts/rvm ; fi

これで、versionの切り替えができるようになった。

andre@andre-VirtualBox:/$ rvm 1.8.7
andre@andre-VirtualBox:/$ ruby -v
ruby 1.8.7 (2012-02-08 patchlevel 358) [i686-linux]
andre@andre-VirtualBox:/$ rvm 1.9.3 andre@andre-VirtualBox:/$ ruby -v ruby 1.9.3p125 (2012-02-16 revision 34643) [i686-linux]

2012年3月20日火曜日

チューリング 不動点演算子 証明

チューリングの不動演算子についての証明を画像に示す。
texでドキュメントをおこそうとしたけど、半年前からまったく触ってなかったので、ドキュメント作成が大変だった(まだできていない)。
現時点では、画像をアップロード。

問題

証明

私のダイエット方法:朝ジョギングダイエット

朝ジョギングダイエット:
https://docs.google.com/open?id=0B8Fi1kuQJgFreGRBV1Jaa25Tei1Ud01hd1ZhbHVXdw

朝食を食べずにジョギングをすることでダイエットする方法を、簡単に書いてあります。
(ジョギング後は朝食食べてOKです。)
ダイエットの1つの方法として、参考にしてください。

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のソース