递归函数中

时间:2017-06-20 15:32:38

标签: ocaml stack-overflow fatal-error

我的Quicksort代码适用于某些N值(列表大小),但对于较大的值(例如,N = 82031),OCaml返回的错误是:

  

致命错误:异常Stack_overflow。

我做错了什么? 我是否应该创建一个迭代版本,因为OCaml不支持大值的递归函数?

let rec append l1 l2 =
  match l1 with
    | [] -> l2
    | x::xs -> x::(append xs l2)


let rec partition p l =
  match l with
    | [] -> ([],[])
    | x::xs ->
      let (cs,bs) = partition p xs in
      if p < x then
        (cs,x::bs)
      else
        (x::cs,bs)


let rec quicksort l = 
  match l with
  | [] -> []
  | x::xs ->
      let (ys, zs) = partition x xs in
      append (quicksort ys) (x :: (quicksort zs));;

1 个答案:

答案 0 :(得分:7)

问题是没有一个递归函数是尾递归的。

尾递归意味着调用者不应该进行进一步的操作(参见here)。在这种情况下,不需要保持调用函数的环境,并且堆栈没有填充递归调用的环境。像OCaml这样的语言可以以最佳方式编译,但为此你需要提供尾递归函数。

例如,您的第一个函数append

let rec append l1 l2 =
  match l1 with
    | [] -> l2
    | x::xs -> x::(append xs l2)

正如您所看到的,在调用append xs l2之后,调用者需要执行x :: ...并且此函数最终不是尾递归。

以尾递归方式执行此操作的另一种方法是:

let append l1 l2 =
  let rec aux l1 l2 =
    match l1 with
      | [] -> l2
      | x::xs -> append xs (x :: l2)
  in aux (List.rev l1) l2

但实际上,您可以尝试使用List.rev_append知道此函数会追加l1l2,但l1会被撤消(List.rev_append [1;2;3] [4;5;6] [3;2;1;4;5;6]

尝试使用尾递归方式转换其他函数,并查看它给你的内容。