OCaml 第一章习题

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 *)

快速幂itrfib没有简单递归的fib速度快的原因可能在于itr不是尾递归? 此处存在疑惑。

求函数的根,二分查找

  1. 使用递归
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;;
  1. 使用 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=xnf(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 是列表,ef 操作的单位元 *)
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);;

其输出如下两种情况即同样的代码会有不同的输出
输出1输出2
不管是哪种输出都不是我所期望的,我希望的输出是同如下代码的输出结果:

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);;

其输出如下,是符合期望的。
输出3
希望有大佬能解答我的问题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个 f4f2f22f2f1f22f14f1f08。​

原因: λ \lambda λ演算的 β \beta β归约只做替换,不存储中间结果?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值