OCaml Lists

这是我参与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对于i1...n之间,那么[e1; ....; en]等价于[v1; ....; vn]

  • If e1 ==> v1, and if e2 ==> v2, then e1 :: e2 ==> v1 :: v2.

  • If ei ==> vi for all i in 1..n, then [e1; ...; en] ==> [v1; ...; vn].

static semantics

列表中所有的元素必须是相同的类型。假设该类型为t, 则这个列表的类型为t list。那么类型检查规则是

  • [] : 为'a list'a代表未知类型。
  • 如果 e1 : te2 : t liste1 :: 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的第一个元素,tlst中其他的元素,并且返回h + sum tht暗含列表的头尾。你也可以换成其他的字母:

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.hdList.tl分别返回列表的头和尾部。

猜你喜欢

转载自juejin.im/post/7031198805089665061