OCaml 第一章练习
文章目录
最大公约数
let rec gcd a b =
if b = 0 then a
else if a < b the n gcd b a
else gcd b (a mod b);;
奇偶判定
相互递归的使用
let rec even x =
if x = 0 then true
else odd (x-1)
and odd x =
if x = 0 then false
else even (x-1)
复合函数
let (@@) f g = fun x -> f (g x);;
(* 二者等价于 lambda f. lambda g. lambda x. f (g x) *)
let (@@) f g x = f (g x);;
函数的n次幂, f n f^n fn
三者等价。
let (@@) f g x = f (g x);;
(* type = (a' -> b') -> (c' -> a') -> c' -> b' *)
let rec itr n f =
let n = 0 then fun x -> x
else f @@ itr (n-1) f
let rec itr n f =
if n = 0 then fun x -> x
else fun x -> f (itr (n-1) f x)
(* type = int -> (a' -> a') -> a' -> a' *)
let rec itr n f x =
let n = 0 then x
else f (itr (n-1) f x)
函数快速幂, f n = f n / 2 ( f n / 2 ) f^n=f^{n/2} (f^{n/2}) fn=fn/2(fn/2)
let sq f x = f (f x);;
let rec itr n f x =
if n = 0 then x
else if n mod 2 = 0 then sq (itr (n/2) f) x
else f (sq (itr (n/2) f) x);;
将 sq 带入得到如下表达式
let rec itr n f x =
if n = 0 then x
else if n mod 2 = 0 then (fun f x -> f (f x)) (itr (n/2) f) x
else f ( (fun f x -> f (f x)) (itr (n/2) f) x );;
统计表达式的计算时间
(* type = (a' -> b') -> a' -> b' *)
let time f x =
let t = Sys.time() in
let fx = f x in
Printf.printf "Execution time: %fs\n" (Sys.time() -. t);
fx;;
(* 使用方法 *)
time (itr 1000000 (fun x -> x*x)) 2;;
柯里化和逆柯里化
let curry f x y = f (x, y);;
(* type = (a' * b' -> c') -> a' -> b' -> c' *)
let uncurry f (x, y) = f x y;;
(* type = (a' -> b' -> c') -> (a' * b') -> c' *)
斐波那契数列
(* 使用了快速幂的itr *)
let fib n = fst (itr n (fun (x, y) -> (y, x+y)) (0, 1));;
(* time fib 50000000 输出时间开销为 3.7s *)
(* 简单递归求解 *)
let fib n (x, y) =
match n with
| 0 -> x
| _ -> fib (n-1) (y, x+y);;
(* time (fib 50000000) (0, 1) 输出时间开销为 0.64s *)
快速幂
itr
的fib
没有简单递归的fib
速度快的原因可能在于itr
不是尾递归? 此处存在疑惑。
求函数的根,二分查找
- 使用递归
let rec dicho f (a, b) eps =
let is_ok (a, b) = abs_float (a -. b) < eps
and do_better (a, b) =
let c = (a +. b) /. 2.0 in
if (f a) *. (f c) > 0. then (c, b)
else (a, c)
in if is_ok (a, b) then (a, b)
else dicho f (do_better (a, b)) eps;;
let x = dicho (fun x -> cos (x /. 2.0)) (1.0, 5.0) 1e-10;;
- 使用
loop
循环(其实loop
也是递归)
let rec dicho f (a, b) eps =
let rec loop p f x = if (p x) then x
else loop p f (f x)
and is_ok (a, b) = abs_float (b -. a) < eps
and do_better (a, b) =
let c = (a +. b) /. 2.0 in
if (f a) *. (f c) > 0.0
then (c, b)
else (a, c)
in
loop is_ok do_better (a, b);;
let x = dicho (fun x -> cos (x /. 2.0)) (1.0, 5.0) 1e-10;;
牛顿下山法
x n + 1 = x n − f ( x n ) f ′ ( x n ) , f ′ ( x ) = f ( x + d x ) − f ( x ) d x x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}, f'(x)=\frac{f(x+dx)-f(x)}{dx} xn+1=xn−f′(xn)f(xn),f′(x)=dxf(x+dx)−f(x)
let newton f x dx eps =
let rec loop p f x = if (p x) then x else p f (f x)
and is_ok x = (abs_float x) < eps
and f' x = (f (x +. dx) -. f x) /. dx in
let step x = x -. (f x /. f' x) in
loop is_ok step x;;
let x = newton (fun x -> log x -. 1.0) 2.7 1e-10 1e-10;;
广义加 fold
(* f 是操作,l 是列表,e 是 f 操作的单位元 *)
let fold f l e =
match l with
| [] -> e
| h :: t -> f h (fold f t e);;
let x = fold (fun x y -> x + y) [1;2;3;4;5] 0;;
let x = fold (fun x y -> x * y) [1;2;3;4;5] 1;;
积分函数 integrate
let rec integrate f a b dx res =
if abs_float (b -. a) < dx then res
else integrate f (a +. dx) b dx (res +. (f a) *. dx);;
let x = integrate (fun x -> 1. /. x) 1. 2. 1e-8 0.;;
第一章疑问
本章的主要问题是关于 itr
表达式,我做了如下的实验:
let sq f x = f (f x);;
let rec itr f n dep x =
print_string "cur dep is ";
print_int dep;
print_string ", n = ";
print_int n;
if n = 0 then x
else if n mod 2 = 0 then sq (itr f (n/2) (dep+1)) x
else f (sq (itr (n/2) (dep+1)) x;;
Printf.printf "2^x = %i\n" (itr 4 (fun x -> x + x) 0 1);;
其输出如下两种情况,即同样的代码会有不同的输出:
不管是哪种输出都不是我所期望的,我希望的输出是同如下代码的输出结果:
let sq x = x *. x;;
let rec power a n y =
print_string "cur dep is ";
print_int y;
print_string ", n = ";
print_int n;
print_newline ();
if n = 0 then 1.
else if n mod 2 = 0 then sq (power a (n/2) (y+1))
else a *. sq (power a (n/2) (y+1));;
Printf.printf "2.^x = %f\n" (power 2. 4 0);;
其输出如下,是符合期望的。
希望有大佬能解答我的问题TT.
进展:其实itr
的输出为如下的二叉树的先序遍历
f 4 的 计 算 需 要 f 2 , f 2 被 算 了 2 次 , f 2 的 计 算 需 要 f 1 , 每 个 f 2 需 要 算 2 个 f 1 , 一 共 4 个 f 1 , 同 理 f 0 有 8 个 f^4的计算需要f^2,f^2被算了2次,f^2的计算需要f^1,每个f^2需要算2个f^1,一共4个f^1,同理f^0有8个 f4的计算需要f2,f2被算了2次,f2的计算需要f1,每个f2需要算2个f1,一共4个f1,同理f0有8个。
原因: λ \lambda λ演算的 β \beta β归约只做替换,不存储中间结果?