OCaml入門1 (* 追記:下の説明でopenとloadの説明を間違えていた。openはloadでパスが通っているモジュール名を書かなくてよくするもの。そのうち本文を直す *) 1: 基本的な使い方  次のようにインタプリタが起動する  ocaml  終了は  #quit;; と打ち込む。  バイトコンパイルは  ocamlc hoge.ml ネイティブコンパイルは  ocamlopt hoge.ml また次のようにすると起動時にファイルを読み込むことができる  ocaml hoge.ml またライブラリを読み込ませたいときは  ocaml unix..cma などとする。デフォルトではocamlのライブラリは /usr/local/lib/ocaml/  にある。 先の例だとここからunix.cma というモジュールを探すわけだが、モジュールの種類によって はここにないものもある。例えばTk モジュールなどはここからさらに /labltk という ディレクトリに入っているのでこれを指定するときには以下のようにしなくてはいけない。  ocaml -I +labltk labltk.cma ちなみにI はエルではなく大文字のアイである。この後の+で探索するディレクトリを追加する。 ちなみにこのディレクトリの追加は存在するかは確認しない。つまり以下のようにしても問題なく起動する。  ocaml -I +hogehogehoge 例えばlabltk.cmaを使おうとして Cannot find file labltk.cma などと 出たらディレクトリの指定が間違っている可能性が高い。  ファイルの読み方は上記のようにインタプリタを起動するときにファイル名を 指定してやれば、それをインタプリタで読み込んだことと同じになるわけだが、インタプ リタを起動してから読み込むことも可能だ。以下のようにする。  #use "hoge.ml";; この例だとhoge.mlというファイルをカレントディレクトリから読み込む。モジュールを読み込むには   #load "unix.cma";;  違うディレクトリのファイルを読み込みたいときは次のようにしてディレクトリを移動する。  #cd "../";; この#cd はどうやらホームディレクトリなどは認識しないようだ。つまり次の指定の仕方はエラー。 #cd "~/sub";; 絶対パスで指定するか、カレントディレクトリを表すドット、一個上のディレクトリを 表すドットドットは使えるのでそれを利用する必要がある。 * (蛇足になるかもしれないが、カレントディレクトリのファイルを 表示させたいのなら次のようにするのが手っ取り早い気がする。  #load "unix.cma";; Unix.system "ls";; system はそのまま文字列をシェルに実行させるシステムコール。)  もう一つの方法として起動時と同様に探索するディレクトリを 追加することもできる。次のようにする。例えばインタプリタ内でlabltk を使いたいときは  #directory "/usr/local/lib/ocaml/labltk";; #load "labltk.cma";; なお# load 命令でモジュールを読み込んだときはプログラム内でモジュール名を 指定してやる必要がある。例えば次のようにUnixモジュールを読み込んだときは  #load "unix.cma";; この中の関数systemを使うときは以下のようにする。  Unix.system "ls";; ただしモジュールの読み込みにはもう一つ方法があり、次のようにすればUnixとつける必要はない。  open Unix;; こうすれば上と同じ関数を  system "ls";; として呼ぶことができる。  ここまでで注意してほしいのはモジュールの呼び出し方では#load では ファイル名を直接文字列で指定するのに対して、openでは文字列ではなく モジュール名を直接指定する。(ちなみにopenはOCaml では予約語)この結果、最初の 文字は大文字になる(モジュール名は大文字で始まる。例えばunix.systemはバツ。 Unix.systemが正しい)。なお#load 命令、open命令共にに、ふつうにユーザーが バイトコンパイルした命令も読むことができる。試してみよう。 次のようなファイルtest1.mlがあったとする。  let func1 a b = a + b;; このファイルがあるディレクトリで次のようにしてバイトコンパイルする。  ocamlc test1.ml そうするとtest.cmo というファイルができているはずだ。再びocaml を起動しこれを読み込んでみる。  ocaml #load "test1.ml";; Test.func1 1 1 ;; 2ときちんと表示されたはずだ。一度#quit;; で終了して今度はopen 命令を試してみる。  ocaml open Test;; func1 1 1;; こんども2と表示されたはずだ。しつこいようだがopen命令では文字列では なくモジュール名を直接指定(今回だとTest)し、関数を呼び出すときはモジュール名 をつける必要がないことに注意してほしい。ここら辺のことは拡張子のところ でもう少しだけ詳しくふれる。 インタプリタ内で# をつけて使う命令をtoplevel directive というが、 これにはほかにもいくつかある。詳しいことはマニュアルの第9章 The toplevel system (ocaml) を読んでほしい。ここでは今までに出てきたのを含 めてまとめてみる。  #quit : インタプリタを終了する  #directory "dir-name": 探索にdir-nameを含める #cd "dir-name": カレントディレクトリをdir-name に変更する  #load "file-name" : バイトコードを読み込む  #use "file-name": ソースコードを読み込む  #trace, #untrace , #untrace_all : トレースするがあまり役に立たない気がする  #labels bool : 関数でラベルを無視するときはこのフラグをfalse にする。 ラベルというのは簡単に言うと、関数で引数を一部だけ指定するのですむようにす るもの。unixモジュール、labltk,lablgtk,lablglなどで普通に使われている。 その時になったら説明する。 2:拡張子  ocamlでは拡張子がやたらと多い。ml,mll,mly,cmo,cma,cmi,cmx,cmxaなどがある。 これらをある程度整理しておかないと混乱する(自分は混乱した)。ので、まとめて整理してみたい。  まず説明不要な気もするが、mlという拡張子がソースファイルだ。いうまでもなく一番基本となるファイルだ。  mll、mlyはそれぞれocamllex、ocamlyaccのソースコードだ。これは名前から 想像できるように字句解析、構文解析で有名なCのlex,yacc のocaml版だ。興味のある人は ソースコードのトップディレクトリ/parsing にocamlを字句解析、構文解析している(多分) ocamllexとocamlyaccのファイルがある。見てみてほしい。が、興味のない人は特に関係ない。  cma,cmxa: ライブラリを表すファイル。コンパイルするときにリンクしたりする。 自分で作ったりする方法は後で紹介するとして、一番大事なのはこれらがバイトコードと ネイティブコードであるという点だ。例えばunixモジュールを使ったプログラム unix_test1.mlをコンパイルしようとする。 (* unix_test1 の内容*)  let _ = Unix.sleep 1;; (* _(アンダーバー)を関数名にするのはこの関数を実行させたいときの決まり文句のようなもの *)  ocamlc unix.cma unix_test1.ml -o unix_test1 実行するには次のように打つ。  ./unix_test1 一秒間止まり、その後にシェルに戻ったはずだ。これと同様なことをネイティブコードで しようとしてもうまくいかない。次のように打つとエラーになる。  ocamlopt unix.cma unix_test1.ml -o unix_test1 ネイティブコードを出すには次のようにしなくてはいけない。  ocamlopt unix.cmxa unix_test1.ml -o unix_test1 ocamlcでやったのと同様に  ./unix_test1 で実行できる。 cmx : ocamlのオブジェクトコード。ocamlopt でコンパイルすると生成される。 リンクや最適化情報が含まれているようだが、消してはまずいということぐらいが分かっていれば十分な気がする。 cmo: ocamlのバイトコード。インタプリタから読み込んだりできる。ocamlc でコンパイルすると生成される。 cmi:ocamlcでもocamloptでも生成されるようだ。コンパイル化されたインターフェース 情報を記録しているが、これもcmxと同様に特に気にする必要はない気がする。  mli: インタフェースが定義されているファイル。これは結構重要。何が重要かというと モジュールの型などが書かれている点が重要だ。モジュール、ライブラリを作る立場の 人間からすれば少し違うのだろうが使う側の人間にとってみれば、インストールしたライ ブラリのmliファイルを読めばどんな関数、型がどのように定義されているかがすぐに分かるという のは非常に便利だ。OCamのように資料が少ない言語ではこの情報は貴重だ。 このようにモジュールのインターフェースを調べるにはmliファイルを見ればよい のだが(標準ライブラリならinfoファイルが一番早いだろうが)、結構面倒だ。 このようなときはocamlbrowserを使うと便利だ。例えばlabltkのインターフェースを調 べたいのなら次のようにディレクトリを指定してやれば、標準のものに加えて labltkのインターフェースがGUIで読める。  ocamlbrowser -I +labltk 拡張子の話のついでというわけでもないが、ここでコンパイルの仕方をまとめ てみたい。基本的にはgcc と同様だ。つまり基本は次の形になる。  ocamlc hoge.ml -o hoge ocamlopt hoge.ml -o hoge なおこれらの最適化のバージョンもある。インストール時の設定をしていると インストールされているはずだ。  ocamlc.opt hoge.ml -o hoge  ocamlopt.opt hoge.ml -o hoge などとする。コマンド名以外はocamlc,ocamloptと同様だ。これからの説明では 基本的に最適化でないほうでするがオプションなども同じなので多分最適化するコン パイラの方でも同様に動くはずだ。 ライブラリを指定するにはソースコードの前にライブラリのファイル名を書けばよい。  ocamlc unix.cma hoge.ml -o hoge ocamlopt unix.cmxa hoge.ml -o hoge 注意事項としてライブラリのファイル名は必ずソースファイルの前に置かなくて はいけない。つまり次のやり方はエラーを起こす。  ocmalc hoge.ml unix.cma -o hoge ocamlopt hoge.ml unix.cmxa -o hoge 標準のディレクトリに含まれないファイルをリンクするには  ocamlc -I +labltk labltk.cma hoge.ml -o hoge ocamlopt -I +labltk labltk.cmxa hoge.ml -o hoge などとすればよい。つまりこれは/usr/local/lib/ocaml/labltkからlabltk.cma , labltk.cmxa を探してリンクしている。 次に分割コンパイルの例を挙げる。 次のようなファイルがあったとする。  (* test1.ml *) let test1 a = a + 1;; (*test2.ml *) let test2 a = a + 2;; これらをそれぞれコンパイルする。ここで-cオプションをすると実行ファイルを作らずコンパイルのみになる。  ocamlc -c test1.ml ocamlc -c test2.ml これを呼び出すには次のようなファイルtest3.mlを使う。  open Test1 open Test2 let _ = print_int (test1 2); print_newline();print_int (test2 2);print_newline();; これをコンパイルするには  ocamlc test1.cmo test2.cmo test3.ml -o test3 とする。 ./test3 として実行すると 3 4 と表示されたはずだ。(ちなみにコンパイルするときにはopenの後に;;はあって もなくてもよい。インタプリタでは必要) openを使わない次のような方法もある。  (* test4.ml *) let _ = print_int (Test1.test1 2);print_newline();print_int (Test2.test2 2);print_newline();; これでも同様にコンパイルできる。  ocamlc test1.cmo test2.cmo test4.ml -o test4 ./test4 3 4 今の例ではバイトコードコンパイルだったが、ネイティブコンパイルでも同様なことができる。  ocamlopt -c test1.ml ocamlopt -c test2.ml ocamlopt test1.cmx test2.cmx test3.ml -o test3 次にプロファイラとデバッガの使い方を説明する。OCamlのプロファイラにはocamlprof というのがついているがあまり役には立たない気がする。またocamloptでコンパイルする ときにオプションをつけると gprofというプロファイラが使えるが、cでコンパイルした とき同程度に役に立つということはないようだ。ここでは両者の使い方を紹介する。  次のようなファイルを用意する。  (* profile_test1.ml) let func1 a = a + 1;; let _ = for i = 1 to 1000 do func1 i done;; これを次のようにコンパイルする。  ocamlcp -p a profile_test1.ml -o profile_test1 警告が出るが多分コンパイルできるはずだ。この警告を出さないようにするには次のようにする。  ocamlcp -p a -w s profile_test1.ml -o profile_test1 もしくはソースファイルを変更する。  let func1 a = a + 1;; let _ = for i=1 to 1000 do ignore(func1 i) donel;; これで普通にコンパイルできる。ignoreは返値を明示的に無視するOCamlの組み込み関数。  次に実行し結果を表示する。  ./profile_test1 ocamlprof profile_test1.ml  ocamlprofはソースファイルを指定することに注意。func1の前に(*1000 *) と 表示されれば成功。何回か繰り返すとその値は加算されていく。 次はgprofを試してみる。 (* profile_test2.ml *)  let func1 () = Unix.sleep 1;; let _ = func1();; これを次のようにコンパイルする。  ocamlopt -p unix.cmxa profile_test2.ml -o profile_test2 ./profile_test2 gprof profile_test2 計測結果はいまいち分からないが、とりあえず出来たと思う。 OCamlのデバッガではgdbなどと違ってソースコードの好きな場所にブレイク ポイントを設定することはできない。ブレイクポイントを設定できる場所をイベントな どと呼ぶ。次のbowtieというのがイベントになる。 1: (f arg)bowtie 関数呼び出しの後 2: fun x y z -> bowtie 関数実行の始め 3: function part 1 -> bowtie expr1 | ... | -> partN -> bowtie exprN パターンマッチ 4: if cond then bowtie else bowtie expr2 条件節 5: while cond do bowtie body done , for i=a to b do bowtie done ループ これらについてそれぞれ確認してみる。 まず次のようなファイルを用意する。最初の数字は行番号になる。 (* debug_test1.ml*) 1: let func1 () = 2: for i=1 to 10 do print_string "a" done;; 3:  4: let _ = func1 ();; 次のようにコンパイルする。 ocamlc -g debug_test1.ml -o debug_test1 デバッガを起動させる。 ocamldebug debug_test1 次のようにして3つのブレイクポイントを設定する。 break @Debug_test1 1 break @Debug_test1 2 break @Debug_test1 4 起動させる。 run まず最初にブレークポイント1でとまり、そこでさらにrunと打ち込んで続けると ブレークポイント2が10回続き、最後にブレークポイント3に帰ってくるのが確認 できると思う。3つ目のブレークポイントが終わった後に再び走らせるには次のように打って プログラムを最初に戻すのが簡単だ。  goto 0 再びrunで実行して2つ目のブレークポイントになったら変数の値を表示させてみよう。 次のように打つ。 print i もしくは display i これは省略記法があってp i , d i と入力しても同じだ。この2つの違いは後で触れる。 ブレークポイントの情報を表示するには  info break と入力する。ブレークポイントを削除するには、例えばinfo で Num が2のブレークポイントを削除したいのなら  delete 2 と入力する。すべてのブレークポイントを削除したいのなら  delete と入力する。デバッガを終了したいときは  quit と入力する。 次にパターンマッチと条件節にブレークポイントが設定できることを確認する。次のようなファイルを用意する。  (* debug_test2.ml *) 1: let func1 a = 2: match a with 3: 1 -> print_int 1; 4: | 2 -> print_int 2; 5: | 3 -> print_int 3; 6: | _ -> print_int  10;; 7: 8: let func2 a = 9: if a = 1 10: then print_int 1 11: else print_int 10;; 12: 13: let _ = func1 1;func1 2;func1 4;func2 1;func2 3;; これを同様に  ocamlc -g debug_test2.ml -o debug_test2 ocamldebug debug_test2 break @Debug_test2 3 break @Debug_test2 4 break @Debug_test2 5 break @Debug_test2 6 break @Debug_test2 10 break @Debug_test2 11 などとしてrunするときちんとブレークポイントが設定できていることを確認できる 次にもう少し詳しくprint とdisplay 命令の使い方を見ていく。 次のようなファイルを用意する。  (* debug_test3.ml) 1: let _ = 2: let a = Array.make 10 0 in 3: let b = Array.make 3 [|2;2;2|] in 4: let c = [| [| [|2;3|] |] |] in 5: if a.(0)=2 then () else ();; これを次のようにして実行する。  ocamlc -g debug_test3.ml -o debug_test3 ocamldebug debug_test3 break @Debug_test3 5 run こうして、まず p a p b p c d a d b d c と打ってみる。print文のほうはすべて表示されたがdisplayの方はbとcはきちんと 表示されなかったはずだ。これはdisplay文が深さ1までのものしか表示しないせいだ。 この深さは例えば次のようにして設定することが可能だ。 set print_depth 2 このようにして p a p b p c とするとcだけがきちんと表示されない。このように表示する深さを設定するこ とができる。また一度に表示する長さも指定することが出来る。例えば次のように入力する。 set print_length 5 こうして p a p b p c d a d b d c と打ってみると、表示されている長さが変わっているのが確認できる。  あとデバッガで知っておいた方がよいコマンドは  set program ファイル名 set arguments 引数 ぐらいだろう。これでプログラムとその引数を指定する。