这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战」
OCaml 中的列表是有相同数据类型的序列。
创建列表
Syntax 有三种语法来创建列表
[]
e1 :: e2
[e1; e2; ...; en]
- [] 为空列表 -> nil
e1 :: e2: 将元素e1放到e2的前面[e1; e2]等价于e1 :: e2 :: []::发音为"cons", 来源于 Lisp
因为列表的元素可以是任意的表达式,所以列表可以被任意的嵌套。
dynamic semantics
- [] 为一个值
- 如果
e1等于v1, 并且e2等于v2, 那么e1 :: e2等价于v1 :: v2
可以推广出来,
-
如果
ei等价于vi对于i在1...n之间,那么[e1; ....; en]等价于[v1; ....; vn] -
If
e1 ==> v1, and ife2 ==> v2, thene1 :: e2 ==> v1 :: v2. -
If
ei ==> vifor alliin1..n, then[e1; ...; en] ==> [v1; ...; vn].
static semantics
列表中所有的元素必须是相同的类型。假设该类型为t, 则这个列表的类型为t list。那么类型检查规则是
- [] : 为
'a list,'a代表未知类型。 - 如果
e1 : t和e2 : t list则e1 :: e2 : t list。
列表访问
只有使用cons 和 nil 来创建列表。如果你想将列表分成几个部分,我们必须区别对待空和非空的情况。我们会使用 模式匹配 来处理列表, 比如:
utop # let rec sum lst =
match lst with
| [] -> 0
| h :: t -> h + sum t;;
val sum : int list -> int = <fun>
这个函数的意思是输入为一个列表lst, 并且看它是不是空列表。如果是空列表则返回0, 如果不是,如果他有和h :: t一样形式的列表,将h认为lst的第一个元素,t为lst中其他的元素,并且返回h + sum t。h和t暗含列表的头尾。你也可以换成其他的字母:
utop # let rec sum xs =
match xs with
| [] -> 0
| x :: xs' -> x + sum xs';;
val sum : int list -> int = <fun>
从语法上镜,没有必要将所有的代码写成几行,我们可以一行代码写完
utop # let rec sum xs = match xs with | [] -> 0 | x :: xs' -> x + sum xs';;
val sum : int list -> int = <fun>
另外,with 后面的第一个|是可以省略的
utop # let rec sum xs = match xs with [] -> 0 | x :: xs' -> x + sum xs';;
val sum : int list -> int = <fun>
下面可以看到如果用这种方法求列表的长度:
utop # let rec length lst =
match lst with
| [] -> 0
| h :: t -> 1 + length t;;
val length : 'a list -> int = <fun>
这个例子中,我们的h是没有用的,我们可以用下划线作为占位符来省略
let rec length lst =
match lst with
| [] -> 0
| _ :: t -> 1 + length t
这个方法其实是OCaml标准空自带的方法,List.length。这里的点标点符号代表函数length在模块名为List中。
下面的例子是将一个列表加到另外一个列表的前面
utop # let rec append lst1 lst2 =
match lst1 with
| [] -> lst2
| h :: t -> h :: append t lst2;;
val append : 'a list -> 'a list -> 'a list = <fun>
可以测试一下:
utop # append [1;2] [3; 4];;
- : int list = [1; 2; 3; 4]
这个函数可以用内置的操作符@实现
utop # [1;2] @ [3; 4];;
- : int list = [1; 2; 3; 4]
最后一个列子,是判断一个列表是否为空
utop # let empty lst =
match lst with
| [] -> true
| h :: t -> false;;
val empty : 'a list -> bool = <fun>
但是有一个不需要使用模式匹配更好的方法,
utop # let empty lst =
lst = [];;
val empty : 'a list -> bool = <fun>
注意的是,上面所有的递归函数的形式,必须要有递归基。
另外,OCaml中有两个库函数,List.hd和List.tl分别返回列表的头和尾部。