Mergesort在F#中出错

时间:2013-05-30 04:29:21

标签: f# mergesort f#-scripting

let rec merge = function
| ([], ys) -> ys
| (xs, []) -> xs
| (x::xs, y::ys) -> if x < y then x :: merge (xs, y::ys)
                    else y :: merge (x::xs, ys)

let rec split = function
| [] -> ([], [])
| [a] -> ([a], [])
| a::b::cs -> let (M,N) = split cs
              (a::M, b::N)

let rec mergesort = function
| [] -> []
| L -> let (M, N) = split L
       merge (mergesort M, mergesort N)


mergesort [5;3;2;1]  // Will throw an error.

我从这里StackOverflow Question获取此代码但是当我使用列表运行mergesort时出现错误:

stdin(192,1): error FS0030: Value restriction. The value 'it' has been inferred to have generic type
    val it : '_a list when '_a : comparison 

我该如何解决这个问题?问题是什么?信息越多越好(所以我可以学习:))

1 个答案:

答案 0 :(得分:3)

您的mergesort函数缺少导致签名由编译器推断为'a list -> 'b list而不是'a list -> 'a list的情况。它应该是'a list -> 'a list的原因是你不打算在mergesort中更改列表的类型。

尝试将mergesort函数更改为此,以解决问题:

let rec mergesort = function
 | [] -> []
 | [a] -> [a]
 | L -> let (M, N) = split L
        merge (mergesort M, mergesort N)

您的代码的另一个问题是,merge和split都不是尾递归的,因此您将在大型列表上获得堆栈溢出异常(尝试调用更正的mergesort,如mergesort [for i in 1000000..-1..1 -> i])。

您可以使用累加器模式

使拆分和合并函数尾递归
let split list =
  let rec aux l acc1 acc2 =
    match l with
        | [] -> (acc1,acc2)
        | [x] -> (x::acc1,acc2)
        | x::y::tail ->
            aux tail (x::acc1) (y::acc2)
  aux list [] []

let merge l1 l2 = 
  let rec aux l1 l2 result =
    match l1, l2 with
    | [], [] -> result
    | [], h :: t | h :: t, [] -> aux [] t (h :: result)
    | h1 :: t1, h2 :: t2 ->
        if h1 < h2 then aux t1 l2 (h1 :: result)
        else            aux l1 t2 (h2 :: result)
  List.rev (aux l1 l2 [])

您可以阅读有关累加器模式here的更多信息;示例在lisp中,但它是一种通用模式,适用于提供尾调用优化的任何语言。