F#合并排序尾递归
我试图为mergesort编写一个尾部递归代码。代码编译并运行。然而,输出是错误的。它只输出一个整数。我想知道如何修复这段代码,以便对整数列表进行排序和输出F#合并排序尾递归,f#,F#,我试图为mergesort编写一个尾部递归代码。代码编译并运行。然而,输出是错误的。它只输出一个整数。我想知道如何修复这段代码,以便对整数列表进行排序和输出 let rec merge L L2 P = match L, L2 with | [], [] -> P | [], _ -> L2 | _, [] -> L | hd::t1, hd2::t2 -> if hd <= hd2 then merge t1 L2
let rec merge L L2 P =
match L, L2 with
| [], [] -> P
| [], _ -> L2
| _, [] -> L
| hd::t1, hd2::t2 ->
if hd <= hd2 then
merge t1 L2 (P @ [hd])
else
merge L t2 (P @ [hd2])
//
// mergesort:
//
let rec ms L =
match L with
| [] -> []
| e::[] -> L
| _ ->
let mid = List.length L / 2
let (L, L2) = List.splitAt mid L
merge (ms L) (ms L2) []
让rec合并l2p=
将L,L2与
|[],[]->P
|[],[uuu2->L2
|_u,[]->L
|hd::t1,hd2::t2->
如果hd[]
|e::[]->L
| _ ->
让中间=列表长度L/2
设(L,L2)=列表。在中间L处拆分
合并(ms L)(ms L2)[]
您的问题在于函数合并
:想象您对列表进行排序[2;1]
。它转向merge[][2][1][
,然后转向merge[][2][1]
,最后第二种情况是match
产生[2]。第二种和第三种情况下的match
必须以某种方式解释P
事实上,您完全不需要在合并中操作3个列表,如果我们将其重构为:
let rec merge l1 l2 =
match (l1,l2) with
| (x,[]) -> x
| ([],y) -> y
| (x::tx,y::ty) ->
if x <= y then x::merge tx l2
else y::merge l1 ty
现在,实现是尾部递归的,易于检查:
let rand = System.Random() in
List.init 1000000 (fun _ -> rand.Next(-10000,10000)) |> ms
>
val it : int list =
[-10000; -10000; -10000; -10000; -10000; -10000; -10000; ...
即使对接受的答案进行了更改,您仍然没有尾部递归合并排序。合并排序的最后一行merge(msl)(msl2)
调用ms
两次,然后调用merge
。为了使函数是尾部递归的,您的函数必须以最多一个对自身的递归调用结束。这种情况下需要继续。通过传递一个continuation,您可以对ms
进行一次调用,并将一个continuation传递给ms
进行第二次调用,然后将第二次调用传递给另一个continuation,该continuation调用merge
。实际上,我会从merge
函数中删除continuation,因为它不是必需的,而且它使merge
函数比使用累加器参数实现更难读取。最后,为了便于从外部调用,我将merge
函数以及ms
函数嵌套在只接受一个列表参数的mergeSort
函数中,不需要向调用方公开其余细节。我在F#中实现的全尾递归合并排序如下:
let mergeSort ls =
let rec merge l1 l2 res =
match l1, l2 with
| [], [] -> res |> List.rev
| x::xs, [] -> merge xs [] (x::res)
| [], y::ys -> merge [] ys (y::res)
| x::xs, y::ys when x < y -> merge xs (y::ys) (x::res)
| xs, y::ys -> merge xs ys (y::res)
let rec ms ls cont =
match ls with
| [] -> cont []
| [x] -> cont [x]
| xs ->
let ys, zs = List.splitAt ((List.length xs) / 2) xs
ms ys (fun ys' -> ms zs (fun zs' -> cont (merge ys' zs' [])))
ms ls id
让mergels排序=
让rec合并l1 l2 res=
将l1、l2与
|[],[]->res |>List.rev
|x::xs,[]->merge xs[](x::res)
|[],y::ys->merge[]ys(y::res)
|当x合并x(y::ys)(x::res)时,x::xs,y::ys
|xs,y::ys->合并xs-ys(y::res)
让记录ms ls cont=
匹配
|[]->cont[]
|[x]->续[x]
|xs->
设ys,zs=List.splitAt((List.length xs)/2)xs
ms ys(fun ys'->ms zs(fun zs'->cont(合并ys'zs'[]))
ms ls id
请注意,在内存使用方面,有一些方法可以更有效地做到这一点,这可能也有助于提高速度,因为内存分配较少,但由于这超出了这个问题的范围,因此我不打算在回答中讨论这一点。merge
在我看来很可疑-当恰好有一个列表为空时,然后,P
不会影响输出。不过,请注意,merge
的版本不再是尾部递归的。很好的捕获,@kvb更新了答案。谢谢你,基思!
let mergeSort ls =
let rec merge l1 l2 res =
match l1, l2 with
| [], [] -> res |> List.rev
| x::xs, [] -> merge xs [] (x::res)
| [], y::ys -> merge [] ys (y::res)
| x::xs, y::ys when x < y -> merge xs (y::ys) (x::res)
| xs, y::ys -> merge xs ys (y::res)
let rec ms ls cont =
match ls with
| [] -> cont []
| [x] -> cont [x]
| xs ->
let ys, zs = List.splitAt ((List.length xs) / 2) xs
ms ys (fun ys' -> ms zs (fun zs' -> cont (merge ys' zs' [])))
ms ls id