近くの図書館 OCaml-Nagoya の「入門 OCaml」があった。 また、五十嵐淳氏の「プログラミング in OCaml」もあった。 両方を借りたので それらをもとに、少しずつ OCaml のことを書こう。
私は Vine Linux 4.1 を使っているが、 インストールした覚えがないのに、 ocaml とコマンドを打つだけで使える。 きっと、最初からインストールされていたのだろう。 バージョンは、3.10.1 である。
ocaml が ghci に比べて劣るのは、コマンド行の編集機能がないことだ。 ghci は、コマンドの履歴を Ctrl-p, Ctrl-n などで呼び出せるのだが、 ocaml にはそれがない。
なお、ocaml でも、関数はテキストファイルに書いて、
テストはトップレベルから呼び出すのがいいと思う。
読み込みは
#use "filename.ml";;
である。
入門 OCaml には、練習問題がない。 サンプルをいじってみる。 なお、いくつかのサンプルは動かないことがある。 http://book.mycom.co.jp/support/bookmook/ocaml/ を参考に訂正することを勧める。
まずは、五十嵐さんの本の練習問題を解いてみよう。 自分なりに仮説を立ててみることにする。
1. と 2. は仮説通り。3. は -3 ではないかと思ったが、-2 が正しい。 丸める方向はどうなっているのだろうか。 4. は 8 を期待していたが、エラーだった。 +3 の + が余計らしい。
今度は問題は省略。考えた解答のみ
3. は、A からアルファベットを順に数えていけばよいことがわかった。 A + 1 がB, ということで、A + 25 が Z、UVXYZ だから、 A + 20 は char 型で 'U' である。
まず、頭の中で予想した。
試した結果はこうだった。
最初の2つはいい線だろう。最後はどういうことだろう。 だいたい、0 という数字を得るにはどうしたいいのかな。 次の2つの方法がある。
第2の解は、あとのほうでわかる。~-.は、float型符号反転用の単項演算子 だ。
次は、先のどちらの教科書にもない問題である。 行列を2重のリストで表したときに、 行列を転置する関数はどうなるか。 関数名を transpose1 としたとき、こうなってほしい。
# transpose1 [[1;2;3];[4;5;6];[7;8;9]];; - : int list list = [[1; 4; 7]; [2; 5; 8]; [3; 6; 9]]
まずは、Haskell で書こうと四苦八苦したが、 どうしてもできない。インターネットで調べたら、 数種類あることがわかった。その中で、 最もシンプルなのが、次のコードだった。
transpose1 :: [[a]] -> [[a]] transpose1 [xs] = [[x] | x <- xs] -- これに気がつかなかった -- transpose1 (xs:xss) = zipWith (:) xs (transpose1 xss)
さて、これを OCaml で書くにはどうするか。 リスト内包表記は、OCaml ではないようだ。 ただし、上の第2行は、 要素をリストで包むためだけのリスト内包表記である。 従って、次のようにしても同じである。
transpose1 [xs] = map (\x -> [x]) xs -- これでも同じ --
よって、OCaml で書く目途がついた。
let rec transpose1 = function [] -> [] | [l] -> List.map (fun x -> [x]) l | v :: l -> List.map2 (fun x y -> x::y) v (transpose1 l)
Haskell と OCaml の対応は次の通り。知らないと困るだろう。
Haskell | OCaml |
---|---|
zip | combine |
unzip | split |
zipWith | map2 |
次に、二つのデータの相関係数を計算する関数を作ってみた。 普通は引数に2つのリストをとるのだろうが、 今回はタプルを要素とするリスト1つを引数とする関数とした。
(* リストの和 *) let sum_list l = List.fold_left (fun x y -> x + y) 0 l;; (* リストの平均 *) let average_list l = float_of_int (sum_list l) /. float_of_int (List.length l);; (* リストの共分散 *) let covariance_list l = (* l はタプルのリスト *) let (l1, l2) = List.split l in let a1 = average_list l1 in let a2 = average_list l2 in let dev p a = float_of_int p -. a in List.fold_left (fun x y -> x +. (dev (fst y) a1) *. (dev (snd y) a2)) 0.0 l ;; let rel_coefficient_list l = (* l はタプルのリスト *) let (l1, l2) = List.split l in let l1_auto = List.combine l1 l1 in let l2_auto = List.combine l2 l2 in covariance_list l /. sqrt (covariance_list l1_auto *. covariance_list l2_auto);;
我ながら冴えないコードだ。
まりんきょ学問所 > 関数型言語手習い > 関数型言語 OCaml