Syntax OCaml合并排序错误?

Syntax OCaml合并排序错误?,syntax,ocaml,mergesort,Syntax,Ocaml,Mergesort,我目前正在学习OCaml,它给我带来了一些问题。我试图实现一个merge_sort函数,但它一直在给定代码的第5行给我一个错误。我只是完全搞不懂为什么它会给我这个错误,如果有人能启发我,我将不胜感激。我甚至不确定我是否正确地设置了模式匹配(匹配语句),所以如果你能看一下,那将非常有用 let rec merge_sorted (l1:int list) (l2:int list) : int list = let end_list = [] in begin match l1, l2 with

我目前正在学习OCaml,它给我带来了一些问题。我试图实现一个merge_sort函数,但它一直在给定代码的第5行给我一个错误。我只是完全搞不懂为什么它会给我这个错误,如果有人能启发我,我将不胜感激。我甚至不确定我是否正确地设置了模式匹配(匹配语句),所以如果你能看一下,那将非常有用

let rec merge_sorted (l1:int list) (l2:int list) : int list =
let end_list = [] in
begin match l1, l2 with 
    | [], [] -> end_list
    | h1::t1, h2::t2 -> if h1 < h2 then merge_sorted t1 t2 (h1 :: end_list) else merge_sorted t1 t2 h2::end_list, h1::end_list
end
让rec合并\u排序(l1:int-list)(l2:int-list):int-list=
让end_list=[]在
开始匹配l1,l2和
|[],[]->结束列表
|h1::t1,h2::t2->如果h1
我得到的错误是:

错误:此函数应用于太多参数, 也许你忘了一个“;” 合并排序:整数列表->整数列表->整数列表

在上面写着“如果h1
我还想知道是否有学习OCaml语法的地方?我一直在尝试使用Jason Hickey的书,但有些东西并没有深入研究(比如多重/并行模式匹配)。我主要是用Java编写代码的,所以用OCaml编写代码对我来说是一种令人沮丧的新体验。

您已经声明了
merge\u sorted
作为类型
int list->int list->int list
的函数,或者一个将两个int list作为参数并返回另一个int list的函数

您遇到的问题是,您正在使用三个
int list
参数调用
merge\u sorted

h1::t1, h2::t2 -> if h1 < h2 then merge_sorted t1 t2 (h1 :: end_list) else merge_sorted t1 t2 h2::end_list, h1::end_list
关于第二个问题,可以找到一套不错的教程

编辑

回应你的评论-

首先,
操作符有两个用途。首先,它创建一个新的列表,其中包含一个新的头和一个现有的尾-
1::[2]
产生
[1;2]
。其次,它在模式匹配期间分解现有列表-
match[1;2]与x::xs
x
绑定到
1
,将
y
绑定到
[2]

在这一点上,您的方法中有一些东西在Java中可以很好地工作,但在OCaml中根本不起作用。首先,在每次递归调用函数时,将
end\u list
声明为空列表

其次,由于列表连接与对
merge\u的递归调用排序在同一行,编译器(别无选择)会认为您正在指定第三个函数调用。你的意思可能是:

h1::t1, h2::t2 -> if h1 < h2 then
                      merge_sorted t1 t2
                      h1 :: end_list
                  else
                      merge_sorted t1 t2
                      h2 :: end_list
                      h1 :: end_list
这是规范的
map
函数,它接受一个列表和一个函数,将列表中的每个项应用于该函数,并返回一个包含结果的新列表。如您所见,将
data
中的每个项目应用到
f
中的结果存储在
acc
中,并将结果传递到下一个调用,只有当
data
为空时才会返回。通过这种方式,我们可以实现一系列列表修改,而无需改变变量

如果不喜欢函数签名中的额外参数,可以将其隐藏在嵌套函数中,如下所示:

let map f data =
    let rec loop d acc =
        match d with
        | []      -> List.rev acc
        | x :: xs -> loop xs ((f x) :: acc)
    in
        loop data []

我希望这对你有一定的意义——从命令式到函数式习惯用法的转换可能会让人费解,对于新手来说,不变性似乎是一个残酷而武断的限制——我保证,如果你坚持下去,函数式编程的好处和美会展现在你面前

本的回答很好,但我希望你不介意多说几句。首先,您的模式匹配并非详尽无遗。它不包括其中一个列表为空而另一个列表为空的情况。第二,如果您刚刚学习OCaml,我将以非尾部递归风格编写合并。也就是说,我不会将累积结果作为参数传递。你会得到大致如下的结果:

let rec merge_sorted l1 l2 =
    match l1, l2 with
    | (* Either one is empty *) -> (* answer is pretty obvious *)
    | h1::t1, h2::t2 ->
          if h1 < h2 then h1 :: (* Merge the rest *)
          else h2 :: (* Merge the rest *)
让rec合并=
将l1、l2与
|(*其中一个是空的*)->(*答案很明显*)
|h1::t1,h2::t2->
如果h1
(我希望这不会泄露太多信息,并从中获取乐趣。)

(新增)

在代码块中:“then h1::(*合并其余*)”,该::是否允许您调用其他函数

好吧,如果你说
(xxx):(yyy)
你可以在
xxx
yyy
部分中调用几乎任何你想要的函数(如果它们在类型上有意义的话)。这实际上是函数式编程的本质:可以用函数调用做任何事情。信不信由你,在你习惯了它之后,很难再回到其他编程方式

我以为那是一个附加到列表前面的操作符

是的,这就是(:)的含义。您希望返回一个由两个输入列表的元素组成的列表,因此您希望使用(:)操作符来建立列表。值得注意的是(::)只是一个函数

另外,就匹配多个列表而言,当其中一个模式匹配(例如“h1::t1,h2::t2->”)之间有一个逗号时,这是否意味着“两个列表都还剩下项目”或“两个列表都还剩下项目”

这意味着这两个项目仍有剩余

还有,为什么h1::t1表示一个非空列表?这只是一个特例吗?我以为::要附加到列表的前面

正如Ben所解释的,(::)被用作操作符和模式组件。当你在模式中使用它时,意味着你有一个有头和尾的列表。尾可以是空的,但头必须是某物。因此模式
h1::t1
只匹配至少1长的列表。
h1
匹配列表的头(an
int
let map f data =
    let rec loop d acc =
        match d with
        | []      -> List.rev acc
        | x :: xs -> loop xs ((f x) :: acc)
    in
        loop data []
let rec merge_sorted l1 l2 =
    match l1, l2 with
    | (* Either one is empty *) -> (* answer is pretty obvious *)
    | h1::t1, h2::t2 ->
          if h1 < h2 then h1 :: (* Merge the rest *)
          else h2 :: (* Merge the rest *)