関数型言語 OCaml

作成日 : 2008-03-16
最終更新日 :

関数型言語 OCaml

近くの図書館 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 のサンプルをいじってみる

入門 OCaml には、練習問題がない。 サンプルをいじってみる。 なお、いくつかのサンプルは動かないことがある。 http://book.mycom.co.jp/support/bookmook/ocaml/ を参考に訂正することを勧める。


プログラミング in OCaml の練習問題を解いてみる

まずは、五十嵐さんの本の練習問題を解いてみよう。 自分なりに仮説を立ててみることにする。

練習問題 2.1

  1. - - 1
  2. - 2+3
  3. 9 / -4
  4. +3 + 5

1. と 2. は仮説通り。3. は -3 ではないかと思ったが、-2 が正しい。 丸める方向はどうなっているのだろうか。 4. は 8 を期待していたが、エラーだった。 +3 の + が余計らしい。

練習問題 2.2

今度は問題は省略。考えた解答のみ

  1. float, 5.5
  2. int, 0
  3. 降参
  4. int, 255
  5. float, 25.0

3. は、A からアルファベットを順に数えていけばよいことがわかった。 A + 1 がB, ということで、A + 25 が Z、UVXYZ だから、 A + 20 は char 型で 'U' である。

練習問題 2.3

まず、頭の中で予想した。

  1. *- は演算子ではない
  2. "0xfg"はintにならない
  3. どこが違うかわからない。もし違うとしたら、- は float ではない、 という

試した結果はこうだった。

  1. Unbound value *-
  2. Exception: Failure "int_of_string"
  3. This expression has type float -> int but here used with type int

最初の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 の対応は次の通り。知らないと困るだろう。

HaskellOCaml
zipcombine
unzipsplit
zipWithmap2

相関係数

次に、二つのデータの相関係数を計算する関数を作ってみた。 普通は引数に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


MARUYAMA Satosi