Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/laravel/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F#合并排序尾递归_F# - Fatal编程技术网

F#合并排序尾递归

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

我试图为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 (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