关于终端解释器

OCaml 和 Python 类似也有终端解释器 utop,输入 utop 后,在 # 后面输入指令(和 Python 的 >>> 一个作用)。输入 quit;; 退出解释器。

如果要使用文件里定义的东西,需要先 use "someFile.ml";;,和 Python import 类似。

Variables and Functions

在 OCaml 里,function 也是 first-class,因此可以像 assign variable 那样把 function assign 给 variable name.

1
2
3
4
let inc x = x+1;;
let x = 1;;
inc x
(* 输出 2 *)

OCaml 使用 (* ...... *) 表示行内注释和跨行注释

OCaml 类型和基础操作

* 整数乘法,*. 浮点数乘法,^ 字符串拼接,"xxxxx".[0] 下标取出字符 (char 类型)

float_of_int: int -> floatint_of_string 字符串转成数字

1
let x = e1 in e2
  1. 计算 e1,赋值给 x
  2. 把值代入 e2,计算的结果作为 let expression 的结果

OCaml 条件控制

1
2
3
if xxxx then yyyy
else if xxxx then yyyy
else yyyy

OCaml 函数

1
2
3
4
5
(* 定义递归函数 *)
let rec factorial x = if x = 0 then 1 else x * factorial (x-1)

(* 两个参数 *)
let rec power x y = if y = 0 then 1 else x * power x (y-1)

Mutually recursive function, 螺旋递归,使用 and 语法进行定义. 本质就是递归直到 base case.

1
2
3
4
5
6
7
(* 
语法:
let rec f x1 ... xn = e1
and g y1 ... yn = e2
*)
let rec even n = n = 0 || odd (n-1)
and odd n = n <> 0 && even (n-1)

匿名函数

1
2
(* 语法:fun x1 ... xn -> e *)
let inc = fun x -> x + 1

pipeline

把上一个函数的输出作为下一个函数的输入。

1
2
3
4
let inc x = x + 1
let sqr x = x * x
(* 使用 |> 记号 *)
5 |> inc |> sqr

Labelled, Optional Arguments

1
2
3
4
5
6
7
8
9
(*
定义了一个函数,两个必填参数和一个可选参数:
在函数体里,用 arg1, arg2 参与运算,但是调用函数时,指定 name1, name2 的参数。
~ 表示必填参数,? 表示可选参数,可选参数需要有默认值

f ~name1:2 ~name2: 4
=> 结果为
*)
let F ~name1:arg1 ~name2:arg2 ?option:(op1=8) = arg1 + arg2 + op1

Tail Recursion Optimization

作为递归函数的优化,考虑

1
let rec count n = if n = 0 then 1 else 1 + count (n-1)

这个函数的问题在于,当 nn 很大的时候,递归可能报栈。Tail Recursion 就是为了优化这一个情况,它要求:

  1. 递归完成返回时,不会对返回的结果进行操作

这样,我们的函数实际上可以重复利用当前的 stack frame 而不用去另开辟一块栈上空间。