読者です 読者をやめる 読者になる 読者になる

YAMAGUCHI::weblog

土足で窓から失礼いたします。今日からあなたの息子になります。 当年とって92歳、下町の発明王、エジソンです。

Objective Caml 入門 手習い(3章 途中まで)

OCaml

相変わらず2日目もOCamlやってますよ。

今度は関数の定義とかの3章です。

Exercise 3,4

問題

Exercise3 2実数の相乗平均をとる関数 geo_mean を定義せよ.
Exercise4 2行2列の実数行列と2要素の実数ベクトルの積をとる関数 prodMatVec を定義せよ. 行列・ベクトルを表現する型は任意でよい.

自分の答え
open Printf

(* Exercise 3 *)
let geo_mean x y = sqrt (x *. y);;

Printf.printf "%f\n" (geo_mean 5.0 10.0);;

(* Exercise 4 *)
let prodMatVec ((x1, y1), (x2, y2)) (x3, y3) = (x1*.x3+.x2*.y3, y1*.x3+.y2*.y3);;

let print_vec (a1, a2) = 
  Printf.printf "(%f, %f)\n" a1 a2;;

print_vec (prodMatVec ((1.0, 2.0), (3.0, 4.0)) (5.0, 6.0));;

Exercise 7,8

問題

Exercise7 x は実数,n は 0 以上の整数として, xn を計算する関数 pow (x,n) を以下の2種類定義せよ.
1. pow の(再帰)呼出しを n 回伴う定義
2. pow の(再帰)呼出しは約 log2 n 回ですむ定義. (ヒント: x2n = (x2)n である.では,x2n+1 = ?)
Exercise8 前問の pow の最初の定義を反復的にした powi を定義せよ. (もちろん引数の数は一つ増える.呼出し方も説明せよ.)

自分の答え
open Printf

(* Exercise 7 *)
let rec pow1 (x, n) =
  if n = 0 then 1
  else x * pow1 (x, n -1)
;;

let rec pow2 (x, n) = 
  let square x_ = x_ * x_ in
  match n with
	0 -> 1
  |	1 -> x
  |	n when n mod 2 = 0 ->
	  square (pow2 (x, n/2))
  |	_ ->
	  (square (pow2 (x, (n-1)/2))) * x
;;

Printf.printf "pow1 -> %d\n" (pow1 (2, 10));;
Printf.printf "pow2 -> %d\n" (pow2 (2, 10));;

(* Exercise 8 *)
let rec powi (x, n, res) =
  if n = 0 then res
  else powi (x, n-1, x * res)
;;

Printf.printf "powi -> %d\n" (powi (2, 10, 1));;

Exercise 11-1,2

問題

Exercise11以下の関数を定義せよ.
1. Euclid の互除法で二整数の最大公約数を求める関数 gcd.
2. テキストの再帰的な定義で (n, m) を求める関数 comb.

自分の答え
open Printf

(* Exercise 11-1 *)

let rec gcd (m, n) =
  match (m, n) with
    (_, n) when n = 0 -> m
  | (m, n) when m mod n = 0 -> n
  | (_, _) -> gcd (n, m mod n)
;;
  

let print_gcd (m, n) =
  let ret = gcd (m, n) in
  Printf.printf "(%d, %d) -> %d\n" m n ret
;;

print_gcd (1071, 1029);;
print_gcd (1029, 1071);;

(* Exercise 11-2 *)

let rec comb (n, m) =
  match (n, m) with
	(n, m) when n * m = 0 -> 1
  |	(n, m) when n = m -> 1
  |	(n, m) when m > n -> comb (m, n)
  |	(_, _) -> comb (n-1, m) + comb (n-1, m-1)
;;

let print_comb (n, m) =
  let ret = comb (n, m) in
  Printf.printf "comb (%d, %d) -> %d\n" n m ret
;;

print_comb (6, 3);;

感想

  • ガードを使うときにパターンが同じ場合はwhenだけつなげられるかな?と思ったけどダメでした
  • 局所関数?でfunctionを使ったら動かなかったのにmatch..withで書いたら動いた。
    • functionを使うときに引数を取ってしまっていた。match..withと形がに似ていておそらく直してなかったのだと思う。

はやく末尾再帰をすらすらと書けるようにしないと、OCamlのCの字にたどり着けない。

教えていただいたこと

id:camlspotter に添削をいただきました。

  • match..with の直後のパターンの前に | があってもよく、これがあると見やすいともっぱらの評判(流行っている)
  • if e then true else false ----> e に書き換えられるよね(たしかに)
  • 数字の比較は (==) 使わないで (=) を使う。( == はポインタ比較)

参照