ライフゲームモジュール
練習のため、http://d.hatena.ne.jp/komamitsu/20090326/1238081086をモジュールにしてみた。
lifegame.mli
type cell = N | C type world val create_world : cell array array -> world val make_next_world : world -> world val disp_world : world -> (cell -> int -> int -> 'a) -> (int -> unit) -> unit
lifegame.ml
type cell = N | C type world = {matrix:cell array array; x_min:int; x_max:int; y_min:int; y_max:int} let loop_matrix ~x_low ~x_high ~y_low ~y_high ~init ~f = let rec loop_y y result = if y > y_high then result else let rec loop_x x result = if x > x_high then result else loop_x (x + 1) (f x y result) in loop_y (y + 1) (loop_x x_low result) in loop_y y_low init let get_cell m x y = m.(y).(x) let set_cell m x y c = m.(y).(x) <- c let int_of_cell = function C -> 1 | N -> 0 let count_arround_cells w x y = let x_low = if x > w.x_min then x - 1 else x in let x_high = if x < w.x_max then x + 1 else x in let y_low = if y > w.y_min then y - 1 else y in let y_high = if y < w.y_max then y + 1 else y in loop_matrix ~x_low ~x_high ~y_low ~y_high ~init:0 ~f:(fun x_idx y_idx result -> let add_point = if x_idx = x && y_idx = y then 0 else (int_of_cell(get_cell w.matrix x_idx y_idx)) in result + add_point) let next_cell arround_cell_count = function | N when arround_cell_count = 3 -> C (* born *) | C when arround_cell_count = 2 || arround_cell_count = 3 -> C (* survive *) | _ -> N (* death *) let create_world src_matrix = {matrix = src_matrix; x_min = 0; x_max = Array.length src_matrix.(0) - 1; y_min = 0; y_max = Array.length src_matrix - 1} let make_next_world w = let next_matrix_empty = Array.make_matrix (w.x_max + 1) (w.y_max + 1) N in let next_matrix = loop_matrix ~init:next_matrix_empty ~x_low:w.x_min ~x_high:w.x_max ~y_low:w.y_min ~y_high:w.y_max ~f:(fun x y matrix -> let cell = get_cell w.matrix x y in let cell_count = count_arround_cells w x y in set_cell matrix x y (next_cell cell_count cell); matrix) in create_world next_matrix let disp_world w func_for_char func_for_line = ignore( loop_matrix ~x_low:w.x_min ~x_high:w.x_max ~y_low:w.y_min ~y_high:w.y_max ~init:w ~f:(fun x y w -> func_for_char (get_cell w.matrix x y) x y; if x >= w.x_max then (func_for_line y); w) )
使い方はこんな感じ.
main.ml
(* ocamlc lifegame.mli * ocamlc -c lifegame.ml * ocamlc -o lifegame unix.cma lifegame.cmo main.ml *) open Lifegame open Printf let orig_matrix = [| [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;C;N;N|]; [|N;N;C;N;N;N;N;N;N;N;N;N;N;N;N;N;C;N;N;N|]; [|N;N;C;N;N;N;N;N;N;N;N;N;N;N;N;N;C;C;C;N|]; [|N;N;C;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;C;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;C;C;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;C;N;N;N;C;C;C;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|]; [|N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;N|] |] let _ = let world = create_world orig_matrix in let rec loop world = printf "--------------------------------------------\n"; disp_world world (fun cell x y -> printf "%c" (if cell = C then '@' else ' ')) (fun y -> printf "\n"); flush stdout; Unix.sleep 1; loop (make_next_world world) in loop world
書いていて気になった点は、type cell = N | C というコードが lifegame.mli, lifegame.ml の両方に出てくること。C のヘッダファイルのように *.mli に書いておけば *.ml のコンパイル時にそれを参照できるのかなぁと思っていたのだけど駄目だった。
OCaml-3.10.2 の otherlibs/unix/unix.ml* を見たら、やはり同じ内容が埋め込まれている(type error)ので、そういうもんかなぁと諦めた。ちょっと残念。