这就是我使用命令式样式在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
如果可能的话,你能将它转换为备用的'功能'样式实现并解释它是如何工作的吗?即使我正在研究列表理解以及目前的所有内容,我也无法想出在这里使用它。
答案 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 []
根据您的命令版本解释此功能:
while
循环,您可以在其中比较两个当前元素,并将较小的元素添加到结果列表中。 while
循环。 i = cnt1
和j = 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类似,我们在函数参数上进行模式匹配。
when
后卫检查哪个第一项较小merge
与较小的项目是取自项目的剩余部分和整个其他列表。因此,如果x
的第一项较小,我会将x
的第一项(在这种情况下为fx
)附加到merge
调用的结果中剩下的x
(xs
)和整个y
(因为y
的第一项更大)。