这是我参与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 ==> vi
for alli
in1..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
分别返回列表的头和尾部。