入出力など,副作用のある計算をするときには式を評価する順番が重要になる。OCaml にもそのための制御構造(control structure)がある。
逐次実行
1つの方法は let ~ in を使うこと。let 以下が評価された後に in 以下が評価される。
# let () = print_string "Hello, " in print_string "world.\n";; Hello, world. - : unit = ()
複数の式を ; で区切って書くと左から実行する。全体の値はいちばん右の式の値。途中の式の値は捨てられる。
# print_string "Hello, "; print_string "world.\n";; Hello, world. - : unit = ()
条件分岐
if をつかう。then 節が unit型の式であるときに限って,else 以下を省略できる。
# if true then print_string "Hello, world.\n";; Hello, world. - : unit = ()
これは,条件が偽なら何もしない,ということ。
# if false then print_string "Hello, world.\n";; - : unit = ()
begin ~ end
; と if では if のほうが結合強度が強く,then節や else節の途中で ; が出てくるとそこでif式全体が終わりだと判断される。
# let f b = if b then print_string "Hello, "; print_string "world.\n";; val f : bool -> unit = <fun>
この関数は引数(=ifの条件)が偽なら “world.\n” だけが出力される(引数に関係ないから)。
# f true;; Hello, world. - : unit = () # f false;; world. - : unit = ()
もし,真の時に “Hello, world\n” を出力し,偽の時には何もしたくないなら括弧で囲む。
# let f2 b = if b then (print_string "Hello, "; print_string "world.\n");; val f2 : bool -> unit = <fun> # f2 true;; Hello, world. - : unit = () # f2 false;; - : unit = ()
または括弧の代わりにbegin ~ endをつかう。こっちのほうが「よいスタイル」だと推奨されているらしい。
# let f3 b = if b then begin print_string "Hello, "; print_string "world.\n" end;; val f3 : bool -> unit = <fun> # f3 true;; Hello, world. - : unit = () # f3 false;; - : unit = ()
繰り返し
while は
while [式1] do [式2] done
という形をしていて,式1が真であるあいだ式2を繰り返す。while を使った fact の例:
# let fact n = let i = ref 1 and res = ref 1 in while (!i <= n) do res := !res * !i; i := !i + 1 done; !res ;; val fact : int -> int = <fun> # fact 5;; - : int = 120
for は
for [変数] = [式1] to [式2] do [式3] done
または
for [変数] = [式1] downto [式2] do [式3] done
という形をしていて,[変数]を整数[式1]から[式2]まで順に束縛しながら[式3]を評価する。for を使って fact を定義してみよう。
# let fact2 n = let res = ref 1 in for i = 1 to n do res := !res * i done; !res ;; val fact2 : int -> int = <fun> # fact2 5;; - : int = 120