我最近开始使用ocaml进行编码,在定义函数返回的内容时,这种编程语言非常明智。我想写一个函数,它使用两个列表作为参数(应该是升序,元素类型为int(,并返回一个列表,其中包含前两个列表的所有元素,也是升序。
到目前为止,我设法做到了以下几点:
let inter l1 l2 =
let rec aux l1 l2 l3=
if List.hd l1<List.hd l2 then aux (List.tl l1) l2 (List.hd l1 :: l3)
else (if List.hd l1>List.hd l2 then aux l1 (List.tl l2) (List.hd l2::l3)
else (if l1 = [] then List.fold_left (fun x y -> y::x) l3 l2
else if l2=[] then List.fold_left (fun x y -> y::x) l3 l1
))
in List.rev (aux l1 l2 []);;
但当我编译它时,它会返回以下错误消息:
Error: This expression has type 'a list
but an expression was expected of type unit
当我调用该函数时,它运行得很好,但它的运行与预期的一样,但让我困扰的是错误消息。知道为什么会出现吗?
附言:我使用Emacs-图阿雷格模式作为文本编辑器和编译器。
if/else
句法结构是一个表达式。整个表达式的类型由分支返回的表达式类型定义。显然,它们必须是同一类型的。如果没有指定else
分支,则假定省略的else分支是类型unit的表达式,基本上if c then e
是if c then e else ()
的简写。
表达式:
if l2=[] then List.fold_left (fun x y -> y::x) l3 l1
实际上是的缩写
if l2=[] then List.fold_left (fun x y -> y::x) l3 l1 else ()
因此OCaml试图将List.fold_left (fun x y -> y::x) l3 l1
与()
统一起来。它们肯定有不同的类型。如果添加一个显式else
分支,那么所有内容都将进行类型检查(不确定正确性(:
let inter l1 l2 =
let rec aux l1 l2 l3=
if List.hd l1<List.hd l2 then aux (List.tl l1) l2 (List.hd l1 :: l3)
else (if List.hd l1>List.hd l2 then aux l1 (List.tl l2) (List.hd l2::l3)
else (if l1 = [] then List.fold_left (fun x y -> y::x) l3 l2
else if l2=[] then List.fold_left (fun x y -> y::x) l3 l1 else []
))
in List.rev (aux l1 l2 []);;
你的沮丧可能是因为对C类编程语言感到舒服。当试图强制使用C编程风格时,OCaml可能会令人沮丧。模式匹配是OCaml的一个非常强大的功能,可以简化您的解决方案:
let rec inter l1 l2 =
match l1, l2 with
| [], _ -> l2
| _, [] -> l1
| (h1 :: t1), (h2 :: t2) ->
if h1 <= h2 then
h1 :: inter t1 l2
else
h2 :: inter l1 t2
第一种模式表示"如果l1是空列表,则返回l2"。第二种模式表示"如果l2是一个空列表,则返回l1"。当测试模式三时,我们知道两个列表都不是空的,所以我们可以对它们的内容进行模式匹配,所以不需要使用list.hd
等。我们使用if语句来确定哪个头部成为新的头部,以及在递归时使用哪些尾部。
随着你对OCaml习惯用法越来越熟悉,这些解决方案就会自然而然地出现。