前エントリではモジュールの定義とシグネチャを与えるのを別にしたけど,いっぺんにすることもできる。というかその方が一般的なのかな。こんな感じ。
module Table1 : TABLE1 = struct ... end;;
takatoh's blog – Learning programming languages.
前エントリではモジュールの定義とシグネチャを与えるのを別にしたけど,いっぺんにすることもできる。というかその方が一般的なのかな。こんな感じ。
module Table1 : TABLE1 = struct ... end;;
モジュールの関数を使うときには[モジュール名].[関数名]とするけど,open宣言をすればモジュール名をつけなくても使えるようになる。
# map (fun x -> x * x) [1;2;3;4];; Characters 0-3: map (fun x -> x * x) [1;2;3;4];; ^^^ Unbound value map # open List;; # map (fun x -> x * x) [1;2;3;4];; - : int list = [1; 4; 9; 16]
複数のモジュールをopenしたとき,同じ名前の関数がある場合には後からopenしたモジュールの関数だけが使えるようになる。たとえば,map関数は ListモジュールにもArrayモジュールにもあるけど,次のようにするとArrayモジュールのほうだけが使える。
# open List;; # open Array;; # map (fun x -> x * 10) [|1;2;3;4|];; - : int array = [|10; 20; 30; 40|]
↑Arrayのmapは使える。↓Listのmapは使えない。
# map (fun x -> x * 10) [1;2;3;4];; Characters 22-31: map (fun x -> x * 10) [1;2;3;4];; ^^^^^^^^^ This expression has type 'a list but is here used with type int array
モジュール名をつけてやれば使える。
# List.map (fun x -> x * 10) [1;2;3;4];; - : int list = [10; 20; 30; 40]
出力も覚えたし書いてみた。
let fizzbuzz x = let f = if x mod 3 = 0 then "Fizz" else "" in let b = if x mod 5 = 0 then "Buzz" else "" in let fb = f ^ b in if fb = "" then string_of_int x else fb ;; let () = for i = 1 to 100 do print_endline (fizzbuzz i) done ;;
実行結果は省略するけど,うまくいったとだけ書いておく。
本文中の関数 whle を参考にして,for式相当の機能を実現する再帰関数を定義しなさい。
こんなんでいいのかな。
# let rec fr frm t body = if frm <= t then begin body frm; fr (frm + 1) t body end ;; val fr : int -> int -> (int -> 'a) -> unit = <fun>
# fr 1 5 (fun x -> print_endline ( string_of_int x));; 1 2 3 4 5 - : unit = ()
本文中でもふれたように,ref型は以下のように定義された,1フィールドの書き換え可能な レコードです。
type 'a ref = { mutable contents : 'a };;関数 ref,前置演算子 !,中置演算子 := の定義をレコードに関連した操作で書きなさい。
こうかな。
let ref x = { contents = x };;
let (!) x = x.contents;;
let (:=) x v = x.contents <- v;;
与えられた参照の指す先の整数を1増やす関数 incr を定義しなさい。
# let incr x = x := !x + 1;; val incr : int ref -> unit = <fun>
# let x = ref 3;; val x : int ref = {contents = 3} # incr x;; - : unit = () # !x;; - : int = 4
参照と繰り返しの構文(while,for)を使ってフィボナッチ数を求める関数を定義しなさい。
# let fib n = let fibs = ref (1, 1) in let i = ref 1 in while !i < n do fibs := (snd !fibs, fst !fibs + snd !fibs); i := !i + 1 done; snd !fibs ;; val fib : int -> int = <fun>
初項を第0項とした。
# fib 0;; - : int = 1 # fib 1;; - : int = 1 # fib 2;; - : int = 2 # fib 3;; - : int = 3 # fib 4;; - : int = 5 # fib 5;; - : int = 8 # fib 6;; - : int = 13
チャネルっていうのは,ファイルディスクリプタみたいなものだと思っておけば良さそう。
入力用には open_in と close_in を使う。
こういうファイル members.txt があったとして:
^o^ >type members.txt andy bill charlie
ファイルから入力する例。
# let infile = open_in "C:/home/takatoh/members.txt";; val infile : in_channel = <abstr> # input_line infile;; - : string = "andy" # input_line infile;; - : string = "bill" # input_char infile;; - : char = 'c' # input_byte infile;; - : int = 104
ファイルの最後に到達すると End_of_file 例外が発生する。
# input_line infile;; - : string = "arlie" # input_line infile;; Exception: End_of_file. # close_in infile;; - : unit = ()
一方,ファイルに出力するには open_out と close_out。
# let outfile = open_out "C:/home/takatoh/foo.txt";; val outfile : out_channel = <abstr> # output_string outfile "Hello, world.";; - : unit = () # output_char outfile '\n';; - : unit = () # close_out outfile;; - : unit = ()
できたファイル:
^o^ >type foo.txt Hello, world.
まとめ
入力 | 出力 | |
オープン | open_in | open_out |
クローズ | close_in | close_out |
1行ずつ | input_line | output_string |
1文字ずつ | input_char | output_char |
1バイトずつ | input_byte | output_byte |
open_out を使ってファイルを開くと,そのファイルがすでに存在した場合,中身を消去してしまう。既存のファイルに追加するには,open_out_gen を使ってチャネルを作る*1。
# let outfile = open_out_gen [Open_wronly; Open_append; Open_text] 0o666 "C:/home/takatoh/foo.txt";; val outfile : out_channel = <abstr>
第1引数は open_flag といって,ファイルを開くときのオプション。上で設定してるのはライトオンリー,追加,テキストファイル,というところだろう。第2引数はファイルのパーミッション,第3引数はファイル名。で,かえってくるのは open_out と同じ出力用のチャネル(out_channel)。
# output_string outfile "foo\n";; - : unit = () # output_string outfile "bar\n";; - : unit = () # close_out outfile;; - : unit = () # open_out_gen;; - : open_flag list -> int -> string -> out_channel = <fun>
foo.txt はこうなる。
^o^ >type foo.txt Hello, world. foo bar
ちゃんと追加になっている。
open_flag はヴァリアントで,The Objective Caml system release 3.10の19.2 Module Pervasives: the initially opened module の General output functionsの項によると:
type open_flag = | Open_rdonly (* open for reading. *) | Open_wronly (* open for writing. *) | Open_append (* open for appending: always write at end of file. *) | Open_creat (* create the file if it does not exist. *) | Open_trunc (* empty the file if it already exists. *) | Open_excl (* fail if Open_creat and the file already exists. *) | Open_binary (* open in binary mode (no conversion). *) | Open_text (* open in text mode (may perform conversions). *) | Open_nonblock (* open in non-blocking mode. *)
*1:ここ,「プログラム in OCaml」に誤植あり。○ Open_wronly,× Open_wonly
繰り返しの構造を関数(再帰関数)にすることもできる。
# let rec whle condition body = if condition () then begin body (); whle condition body end ;; val whle : (unit -> bool) -> (unit -> 'a) -> unit = <fun>
condition () が真であるあいだ body を繰り返す。条件 condition () が単なる真偽値の式ではなくて unit -> bool 型の関数なところがミソ。たとえばファイルからの入力など。
もう一つ,リストの各要素に対して繰り返す関数。
# let rec iter f = function [] -> () | a :: rest -> begin f a; iter f rest end ;; val iter : ('a -> 'b) -> 'a list -> unit = <fun>
リストの各要素を出力するときとかに使える。