Ocaml中的递归

时间:2014-02-07 12:25:11

标签: recursion ocaml

我是Ocaml的新手,我正在尝试编写一个递归函数。

该函数获取对列表并返回一对列表,例如

[(1, 4); (2, 3); (5, 9); (6, 10)]) -> ([1; 2; 5; 6], [4; 3; 9; 10])

但是编译器说:Error: This expression has type 'a list * 'b list but an expression was expected of type 'a list

在第(unzip (List.tl m))

有人可以解释为什么我有这个错误吗?无论如何要解决这个问题吗?非常感谢你!

let rec unzip m =
    if List.length m = 0 then
       ([], [])
    else
       ((fst (List.hd m)) :: (unzip (List.tl m)), (snd (List.hd m)) :: (unzip (List.tl m)))
in 
    unzip m;; 

2 个答案:

答案 0 :(得分:3)

对于任何递归,您必须注意输出类型将始终相同。

让我们看看您的unzip功能。

[(1, 4); (2, 3); (5, 9); (6, 10)]) -> ([1; 2; 5; 6], [4; 3; 9; 10])

简单地说,unzip的返回类型是def 一对(元组),每个元素都是一个列表,对吗?


然后让我们看看你的代码

let rec unzip m =
    if List.length m = 0 then
       ([], [])
    else
       ((fst (List.hd m)) :: (unzip (List.tl m)), (snd (List.hd m)) :: (unzip (List.tl m)))
in 
    unzip m;;

你有两个分支。第一个分支返回([], [])。好的,就返回类型而言,它是正确的,因为它是一对有两个空列表并匹配上述返回类型。


第二个分支

((fst (List.hd m)) :: (unzip (List.tl m)), (snd (List.hd m)) :: (unzip (List.tl m)))

是正确的吗?

这是一对有两个元素,没问题,然后让我们看第一个元素:

(fst (List.hd m)) :: (unzip (List.tl m))

您正尝试将(fst (List.hd m))添加到(unzip (List.tl m))的头部。

但是你只能使用::向列表添加内容,所以ocaml假设(unzip (List.tl m))是一个列表,对吗?

但它是一个unzip函数应用程序,显然在开头描述,你的unzip没有返回一个列表,而是一对(元组)。

所以ocaml不明白并因此抱怨。


以上只是回答您关于类型问题的问题。但是你的代码有更多问题。

<强> 1。错误使用in

假设您有一个函数f1。您可以将其成像为母功能,这意味着它可以直接使用。同样在f1中,您可以声明另一个函数或变量(或者更正式地说,绑定)。只有在函数内声明绑定时,才使用let...in...。如果您只有母函数,则不要使用in,因为in where

unzip中,您只有一个unzip本身的功能或绑定,它位于顶层。所以in不是必需的。

<强> 2。不正确的递归逻辑

我不知道如何向你解释这里的递归,因为它需要你阅读更多并练习更多。

但你想法中的正确代码是

let rec unzip = function
  | [] -> ([], [])
  | (x,y)::tl -> 
    let l1, l2 = unzip tl in
    x::l1, y::l2

如果你追求的是更好的或尾部递归的版本,那么它是:

let unzip l = 
  let rec unzip_aux (l1,l2) = function
    | [] -> List.rev l1, List.rev l2
    | (x,y)::tl -> unzip_aux (x::l1, y::l2) tl
  in 
  unzip_aux ([],[]) l

答案 1 :(得分:1)

错误来自于(unzip ...)返回一对列表('a list * 'b list)这一事实,您在编写(fst ..) :: (unzip ...)时尝试将其作为列表进行操作。

如果您使用模式匹配,这一切都会写得更好。骨架:

let rec unzip = function
  | [] -> ...
  | (x,y) :: rest ->
    let (xs, ys) = unzip rest in ...