「プログラミング in OCaml ~関数型プログラミングの基礎からGUI構築まで~」を読んでの勉強記録です。
今回は、本に記載されている練習問題を解いていきます。
練習問題 5.2 次の関数を定義しなさい。
5.5 練習問題,p106-107
以下に与えられた9つの関数を定義していきます。
正の整数nから1までの降順リストを生成する関数
downto1
。
# let downto1 n = let rec downto1' i l = if i > n then l else downto1' (i + 1) (i :: l) in downto1' 0 [];; val downto1 : int -> int list = <fun> # downto1 5;; - : int list = [5; 4; 3; 2; 1; 0]
このくらいならすぐに書けました。一応、末尾再帰にしてあります。今後も練習のため末尾再帰可能なものについてはなるべく末尾再帰で書いていきます。
与えられた正の整数のローマ数字表現(文字列)を求める関数
roman
(I = 1,V = 5,X = 10,L = 50,C = 100,D = 500,M = 1000です)。
# let roman dict n = let rec roman' dict n s = let rec find f l = match l with | (a', b) :: rest -> if f a' then (a', b) else find f rest in match n with | 0 -> s | _ -> let (i, r) = find (fun x -> n >= x) dict in roman' dict (n - i) (s ^ r) in roman' dict n "";; (* 警告を省略 *) val roman : (int * string) list -> int -> string = <fun>
find関数はfが真となる要素をリストlの中から見つける関数ですが、要素が見つからない場合については定義してありません。そのため、find関数のマッチ式が網羅的でないという警告が出ています。今は例外処理のやり方がわからないので無視しておきます。以下は実行結果です。
# let romans = [(1000, "M"); (500, "D"); (100, "C"); (50, "L"); (10, "X"); (5, "V"); (1, "I")];; (* romansの表示を省略 *) # roman romans 1984;; - : string = "MDCCCCLXXXIIII" # let romans = [(1000, "M"); (900, "CM"); (500, "D"); (400, "CD"); (100, "C"); (90, "XC"); (50, "L"); (40, "XL"); (10, "X"); (9, "IX"); (5, "V"); (4, "IV"); (1, "I")];; (* romansの表示を省略 *) # roman romans 1984;; - : string = "MCMLXXXIV"
辞書を入れ替えることでより正確な表記を行います。
与えられたリストのリストに対し、(内側のリストの)要素の総数を返す関数
nested_length
。
予め、与えられたリストの長さを返す関数lengthを定義しておきます。
# let length l = let rec length' l n = match l with | [] -> n | _ :: rest -> length' rest (n + 1) in length' l 0;; val length : 'a list -> int = <fun>
続いてnested_lengthを定義します。
# let nested_length ll = let rec nested_length' ll n = match ll with | [] -> n | l :: rest -> nested_length' rest (n + length l) in nested_length' ll 0;; val nested_length : 'a list list -> int = <fun> # nested_length [[1; 2; 3]; [4; 5]; [6]; [7; 8; 9; 10]];; - : int = 10
確かに計算できています。
今回はここまで。次回もリストの練習問題をいくつか解いていきます。