本文用于课程《程序设计语言原理》的学习。
参考:乔治会写代码了吗 的个人主页 - 动态 - 掘金
官网:Welcome to a World of OCaml

OCaml is a general-purpose, industrial-strength programming language with an emphasis on expressiveness and safety.

OCaml 最早称为 Objective Caml,是 Caml 编程语言的主要实现,开发工具包含交互式顶层解释器,字节码编译器以及最优本地代码编译器。Ocaml 有一个巨大标准库,使得可以像 Python 或者 Perl 语言一样可以方便地开发各种应用程序。

基本语法

基本运算

整型操作需要使用:+-*/(向下取整)
浮点数操作需要使用:+.-.*./.
区域操作:mod
布尔运算:&&||not
等于和不等于:=<>
字符串相加:^(字符为 'a',而字符串为 "a"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3 + 3;;

3.0 +. 2.4;;

4 mod 2;;

4.3 mod_float 3.2;;

1 || 2;;

3 = 3;;

3 <> 4;;

"a" ^ "b";;
类型转换

固定格式:DEST_of_ORIG
例如:int_of_float 表示浮点数到整型的转换

函数

在 OCaml 中,使用关键字 let 定义一个函数

  • 函数定义:
1
2
3
4
5
6
7
8
9
  (*不指定类型的函数 参数之间由空格空开 等号为函数返回值*)
let f x y = x + y;;

(*指定类型的函数*)
let f (x : int) (y : int) : int = x + y

(*同时定义多个函数,使用 and 连接*)
let identite x = x and carre x = x * x and cube x = x * x * x

  • 函数调用:

    1
    f 1 2;;
  • 使用 let 定义函数常量

1
let pi = 3.1415926
  • 关键字 in
1
2
3
4
5
6
7
8
9
10
11
12
13
(*不能使用靠前的函数来定义后面的函数 下面语法会报错*)
let carre x = x * x and cube x = (carre x) * x;;
(*使用关键字 in 可以使用前面定义好的函数*)
let carre x = x * x in
cube x = (carre x) * x;;

(*示例函数,用来求两个浮点数的平方和的平方根*)
let norme (x : float) (y : float) :float =
let carre a = a * a in
let x_carre = carre x in
let y_carre = carre y in
sqrt (x_carre + y_carre);; (*sqrt是内置求平方根函数*)

条件及匹配模式

if 和 else

OCaml中的条件语句是作为表达式来用的,最终会得到一个结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(*x为bool类型的表达式 a、b类型要一致 else必须有!*)
(*示例1:if-else*)
if x then a else b

(*示例2:if-else if-else*)
if x then
a
else if y then
b
else
c

(*示例3:将返回结果赋给变量*)
let str = if true then
"hello"
else
"world"

(*示例4:使用圆括号包住嵌套的条件语句*)
let number_departement = 38
let name_departement = if number_departement = 38 then
"Isere"
else (
if number_departement = 19 then
"Correze"
else
"Unknown"
)
匹配模式

除了 if/else 之外,OCaml 还为我们提供另一种更强有力的创造条件的方式:匹配模式(有点像其他语言中 switch 的感觉)。通常而言是列出所有可能的情况,并将一种情况与一个值联结在一起,这和 if/else 是有些相像的。 匹配模式有趣的地方就在于,它可以准确地辨认出模式,而不仅仅是简单的等式。

OCaml中,匹配模式的语法会使用关键词 matchwith

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(* x:将要被测试的值,如果值与列表中的某一项相匹配就返回对应情况的结果 *)
match x with
| case1 -> result1
| case2 -> result2

(* 示例,返回结果为:"Isere" *)
let number_departement = 38
let name_departement =
match number_departement with
| 1 -> "Ain"
| 19 -> "Correze"
| 38 -> "Isere"

(*
如果未找到相匹配的值,则会报错:Exception: Match_failure,为了避免这种情况,可以加一个 _ 来表示默认情况
*)

let name_departement number =
match number with
| 1 -> "Ain"
| 19 -> "Correze"
| 38 -> "Isere"
| _ -> "Unknown"

建立数据模型

我们目前为止只是使用了一些简单的数据类型,如果实现更复杂的功能,就要自己创建更为复杂的数据类型(类似于其他语言中的类或者结构体)

同义类型

类似于取别名

1
2
3
4
5
6
7
8
9
10
(* 基本语法 *)
type NAME = TYPE

(* 举例 *)
type speed = float
type time = float
type distance = float

let s (d : distance) (t : time) :speed = d /. t
(* 函数类型:distance -> time -> speed *)
乘积类型

乘积类型是将几个不同的值组合起来,我们也称之为多元组/元组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(* 基本语法 *)
type NAME = TYPE*TYPE*TYPE

(* 定义乘积类型, 时间类型:时:分:秒 *)
type time = int * int * int

(* end_of_class 类型跟 time 类型是等价的 *)
let end_of_class = (16, 45, 0)
(* 类型为 int*int*int *)

(* 也可以解构使用,类似python拆箱 *)
let (hour, minute, second) = end_of_class

(* 直接在函数的参数中使用解构方法 *)
let add_a_hour (hour, minute, second: time) = (hour + 1, minute, second)

(* 模式匹配中使用解构方法 *)
let meal (hour: time) : string =
match hour with
| (7, _, _) | (8, _, _) -> "Breakfast"
| (12, 0, 0) -> "Lunch"
| (16, 30, 0) -> "Snack"
| (19, 30, 0) -> "Dinner"
| (h, _, _) -> (string_of_int h) ^ "hours? It's not time to eat!"
加法类型
1
2
3
4
5
(* 基本语法 *)
type NAME = CONSTRUCTORS_1 |CONSTRUCTORS_2

(* 例子 *)
type size = Small | Medium | Large
关联数据和constructor

我们还可以选择将信息和constructor关联在一起。为了能够更精确描述信息们的类型,我们在constructor的名字后使用关键词ofof后面可以接我们的目标类型。

1
type resultat = | Ok of float (* 我们将float与builder OK关联 *) | Error (* 我们可以使用这个类型来实现一个不会崩溃的除法函数。 * 如果出现错误,它将会返回Error,处理除法失败的情况也是必要的 *) let div (x : float) (d : float) : resultat = if d = 0.0 then Error (* Division by 0 impossible *) else Ok(x /. d) (* 就是在这里使用与数据关联的builder *)