Merge sort for f sharp

。_饼干妹妹 提交于 2019-12-01 08:41:59

In both your functions you had the problem, that the last step you take is not the recursive call but some other thing:

  • in merge it is the :: operation
  • in mergesort it is the merge

So you have to get to a point where the very last thing is the recursive call!

One possibility in situations where you have more than one recursive call to make is to use continuations - the idea is to pass a function around that should be called with the result of the current step and then continue the computation from there.

this is a tail-recursive version of mergesort using this technique:

let mergesort xs = 
    let rec msort xs cont = 
        match xs with
        | []    -> cont []
        | [x]   -> cont xs
        | _     -> 
            let mid = List.length xs / 2
            let (xs', xs'') = List.splitAt mid xs
            msort xs' (fun ys' -> msort xs'' (fun ys'' -> cont (merge ys' ys'')))
    msort xs id

as you can see the idea is not to hard - instead of first calling both recursive paths it starts with just one half but adds a continuation that basically says:

once I have the result of mergesort xs' I take the result ys' and continue by mergesorting xs'' and then merge those

of course the second step is done in just the same way (push the merge into the continuation)

the very first continuation is usually the identity as you can see in the very last line ;)


and here is something similar for your merge:

let merge xs ys = 
    let rec mrg xs ys cont =
        match (xs, ys) with
        | ([], ys) -> cont ys
        | (xs, []) -> cont xs
        | (x::xs', y::ys') ->
            if x < y 
            then mrg xs' ys (fun rs -> cont (x::rs))
            else mrg xs ys' (fun rs -> cont (y::rs))
    mrg xs ys id

those will of course take as much space on the heap (probably more) - but that is usually no problem - your stack should be fine ;)

Tony Lee

Each recursive call requires stack space. The more times mergesort calls itself, the more stack is used.

You avoid the stack overflow with recursive function by taking advantage of tail recursion. It simply means the last thing a function does is call itself, the call is removed and turns into a jump instead, saving stack space.

This is tricky to do in your case because you have to call mergesort twice. Only one of them can be last. The solution is to use a continuation. You only call mergesort once, but pass it a function to call, which will call mergesort the second time.

Search the internet for F# examples of a merge sort that uses continuations.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!