如何将此命令式样式合并排序实现转换为功能样式?

时间:2012-09-09 15:01:07

标签: f# mergesort

这就是我使用命令式样式在F#中实现merge-sort的方法:

let merge (l1: List<string>,  l2: List<string>) = 
 let r: List<string> = new List<string>()
 let mutable (i,j, cnt1, cnt2) =  (0,0, l1.Count, l2.Count)
 while i < cnt1 && j < cnt2 do
    if l1.[i] <= l2.[j] then
        r.Add (l1.[i])
        i <- i + 1
    else
        r.Add (l2.[j])
        j <- j + 1

 if i = cnt1 then
    while j < cnt2 do
        r.Add (l2.[j])
        j <- j + 1
 else    
    while i < cnt1 do
        r.Add (l1.[i])
        i <- i + 1
 r 

如果可能的话,你能将它转换为备用的'功能'样式实现并解释它是如何工作的吗?即使我正在研究列表理解以及目前的所有内容,我也无法想出在这里使用它。

2 个答案:

答案 0 :(得分:4)

您正在使用.NET List<'T>,它在F#中重命名为ResizeArray<'T>以避免混淆。如果使用功能列表,merge函数将如下所示:

let merge(xs, ys) =
    let rec loop xs ys acc =
        match xs, ys with
        | [], [] -> List.rev acc (* 1 *)
        | [], y::ys' -> loop xs ys' (y::acc) (* 2 *)
        | x::xs', [] -> loop xs' ys (x::acc) (* 3 *)
        | x::xs', y::_ when x <= y -> loop xs' ys (x::acc) (* 4 *)
        | _::_, y::ys' -> loop xs ys' (y::acc) (* 5 *)
    loop xs ys []

根据您的命令版本解释此功能:

  • 第4和第5个模式对应于第一个while循环,您可以在其中比较两个当前元素,并将较小的元素添加到结果列表中。
  • 第二个和第三个模式类似于第二个和第三个while循环。
  • 第一种模式是i = cnt1j = cnt2,我们应该返回结果。由于新元素总是预先附加到累加器,我们需要将其反转以按递增顺序获取列表。

准确地说,您的merge函数只是合并排序算法的一部分。您需要一个函数将列表分成两半,在两半上调用merge-sort并将两个已排序的一半合并为一个。下面的split功能会留给您作为练习。

let rec mergeSort ls =
    match ls with
    | [] | [_] -> ls
    | _  -> let xs, ys = split ls
            let xs', ys' = mergeSort xs, mergeSort ys
            merge(xs', ys')

答案 1 :(得分:1)

为pad添加一个更简单但天真的替代方案:

let rec merge x y = 
    match (x, y) with
    | ([], []) -> []
    | ([], rest) -> rest
    | (rest, []) -> rest
    | (fx :: xs, fy :: _) when fx <= fy -> fx :: merge xs y
    | (fx :: _, fy :: ys) -> fy :: merge x ys

与pad类似,我们在函数参数上进行模式匹配。

  • 我首先将它们放入元组中,以便我可以同时对它们进行模式匹配。
  • 然后我处理base cases,其中两个或任一参数为空。
  • 然后我使用when后卫检查哪个第一项较小
  • 我终于采取的第一项和缺点到另一呼叫的结果merge与较小的项目是取自项目的剩余部分和整个其他列表。因此,如果x的第一项较小,我会将x的第一项(在这种情况下为fx)附加到merge调用的结果中剩下的xxs)和整个y(因为y的第一项更大)。