14.2 分割コンパイル例
前節のシナリオに従い,乱数を使うおみくじプログラムを例に,分割コ ンパイルをして実行形式ファイルを作ってみましょう. システムを
-
•
random:乱数発生器
-
•
main:メインパート
に分割することにします. まず,このインターフェイスファイルを以下のように設計します.
-
•
random.smiの定義.
structure Random =
struct
val intit : int * int -> unit
val genrand : unit -> int
endこのインターフェイスファイルは,このインターフェイスファイルを実 装するソースファイルが,他のファイルは参照せずにRandom structureを提 供することを表しています.
-
•
main.smiの定義.
_require "basis.smi"
_require "./random.smi"このインターフェイスファイルは,SML#の基本ライブラリ"basis.smi"(The Standard ML Basis Library)とこのディレクトリにある random.smiを利用し,外部には何も提供しないことを意味しています.
このインターフェイスを使えば,main.smlとrandom.smlを独 立に開発できます. main.smlは図14.1のように定義できます. このファイルは,random.smiを実装するソースファイルが存在し なくても,コンパイルしエラーがないか確認することができます.
$ smlsharp -c main.sml
-cはSML#コンパイラにコンパイルしオブジェクトファイ ルを生成を指示します. 対話型での使用と同様に型チェックをした後,エラーがなければ, オブジェクトファイルmain.oを作ります. インターフェイスファイルは,ファイル名の.smlを.smiに変 えたものが自動的に使用されます. ソースコード冒頭に_interface 宣言を書くことにより インターフェースファイルを明示的に指定することもできます.
次に,random.smlを開発します. 高品質の乱数発生関数の開発は,数学的な知識と注意深いコーディング が要求されます. ここでは,これはスクラッチから開発するのではなく,既存のCでの実 装を使うことにします. 種々ある乱数発生アルゴリズムの中で,その品質と速度の両方の点から Mersenne Twisterが信頼できます. このアルゴリズムはCのソースファイルmt19937ar.cとして提供されています.
そこでこのファイルをダウンロードしましょう. インターネットでMersenne Twisterあるいはmt19937ar.cをサーチすれば簡単に見つけることができます. このファイルには以下の関数が定義されています.
void init_genrand(unsigned long s);
void init_by_array(unsigned long init_key[], int key_length);
unsigned long genrand_int32(void);
long genrand_int31(void);
double genrand_real1(void);
double genrand_real2(void);
double genrand_real3(void);
double genrand_res53(void);
int main(void);
この中のmainは,このアルゴリズムをテストするためのメイン関 数です. 我々は新たな実行形式プログラムを作成するので,main関数は, 我々のトップレベルファイルmain.smlをコンパイルしたオブジェクトファ イルに含まれているはずです. そこで,mt19937ar.cファイルの関数int main(void)の定 義をコメントアウトする必要があります. その他の関数は,SML#から利用できるライブラリ関数です. ここでは以下の2つを使うことにします.
-
•
void init_genrand(unsigned long s). シーズの長さを受け取りアルゴリズムを初期化する関数です. シーズsは非負な整数ならなんでも構いません.
-
•
long genrand_int31(void).初期化された後は,呼ばれる毎に31ビッ トの符号なし整数(32ビット非負整数)のランダムな列を返します.
そこで,これを利用して,random.smlを以下のように定義します.
structure Random =
struct
val init = _import "init_genrand" : int -> unit
val genrand = _import "genrand_int31" : unit -> int
end
このソースファイルも,以下のコマンドにより,このファイルだけで単 独にコンパイルできます.
$ smlsharp -c random.sml
このソースファイルの定義と並行して,(main関数をコメントア ウトした)Mersenne Twisterをコンパイルし,オブジェクトファイルを生 成しておきます.
$ gcc -c -o mt.o mt19937ar.c
以上ですべてのソースファイルがそれぞれコンパイルされ,オブジェク トファイルが作られたはずです. それらオブジェクトファイルは,トップレベルのインターフェイスファ イルと必要なオブジェクトファイルを指定することによって,実行形式ファイル が作成されます.
$ smlsharp main.smi mt.o
SML#コンパイラは,smiファイルを解析し,このファイル から参照されているsmiファイルを再帰的にたどり,対応するオブジェク トファイルのリストを作り,コマンドラインに指定されたC言語のオブジェクト ファイルと共に,システムのリンカーを起動し,実行形式ファイルを作成します.
fun main() =
let fun getInt () = case TextIO.inputLine TextIO.stdIn of NONE => 0 | SOME s => (case Int.fromString s of NONE => 0 | SOME i => i) val seed = (print "好きな数を入力してください(0で終了です)."; getInt()) in if seed = 0 then () else let val _ = Random.init seed; val oracle = Random.genrand() val message = "あなたの運勢は," ^ (case oracle mod 4 of 0 => "大吉" | 1 => "小吉"| 2 => "吉" | 3 => "凶") ^ "です.\n" val message = print message in main () end end val _ = main(); |