由于这是一个调试问题,我可以通过以下方式找到问题:
首先,我运行程序并意识到它引发了Out_of_memory 异常。所以我知道有一些无限递归正在进行。在某个地方,递归调用应该会遇到基本情况,但事实并非如此,而是会调用自身,直到最终内存不足。
该函数由多个辅助函数组成。看看它们是否都按预期工作。
因为merge是作为merge_sort的内部函数嵌入的,所以很难单独测试,因为你不能直接引用它,如果你把它移出来,它会编译失败,因为它期望比较函数f 来自其父范围。因此,出于可测试性目的,我将稍微更改 merge。
split 函数不需要类似的修改,而real 函数似乎有点不必要,因为它只是合并排序函数; let 中的 let 似乎也没有必要,但是由于我将所有辅助函数移出,因此无论如何都会将其删除。所以我要删除 real 并称之为 mergeSort。
结果(将nil 重新命名为[] 等等,这只是我的偏好):
fun merge p ([], ys) = ys
| merge p (xs, []) = xs
| merge p (x::xs, y::ys) =
if p (x,y) then
x::merge p (xs, y::ys)
else
y::merge p (x::xs, ys)
fun split [] = ([], [])
| split [x] = ([x], [])
| split (x::y::xys) =
let
val (lo, hi) = split xys
in
(x::lo, y::hi)
end
fun mergeSort p [] = []
| mergeSort p zs =
let
val (xs, ys) = split zs
in
merge p (mergeSort p xs, mergeSort p ys)
end
对此进行测试,它仍然会引发Out_of_memory 错误,所以我实际上并没有修复任何东西。
让我们尝试在一个小的输入上手动运行它;
- 下面以
= 开头的每一行都表示一个术语重写,我用它的定义替换了前表达式的某些部分。例如,作为起点的mergeSort (op >) [1,2,3] 在与[1,2,3] 模式匹配的输入上被替换为mergeSort 的定义。
- 以 `- 开头的每一行都是我尝试扩展表达式的子部分,而不包括在上述表达式的完全重写中 - 否则可能会有点混乱.
mergeSort (op >) [1,2,3]
= let val (xs, ys) = split [1,2,3] in merge (op >) (mergeSort (op >) xs, mergeSort (op >) ys) end
`- split [1,2,3]
= let val (lo, hi) = split [3] in (1::lo, 2::hi) end
= let val (lo, hi) = ([3], []) in (1::lo, 2::hi) end
= (1::[3], 2::[])
= ([1,3], [2])
= let val (xs, ys) = ([1,3], [2]) in merge (op >) (mergeSort p xs, mergeSort p ys) end
= merge (op >) (mergeSort (op >) [1,3], mergeSort (op >) [2])
`- mergeSort (op >) [1,3]
= let val (xs, ys) = split [1,3] in merge (op >) (mergeSort (op >) xs, mergeSort (op >) ys) end
`- split [1,3]
= let val (lo, hi) = split [] in (1::lo, 3::hi) end
= let val (lo, hi) = ([], []) in (1::lo, 3::hi) end
= (1::[], 3::hi)
= ([1], [3])
= let val (xs, ys) = ([1], [3]) in merge (op >) (mergeSort (op >) xs, mergeSort (op >) ys) end
= merge (op >) (mergeSort (op >) [1], mergeSort (op >) [3])
`- mergeSort (op >) [1]
= let val (xs, ys) = split [1] in merge (op >) (mergeSort (op >) xs, mergeSort (op >) ys) end
= let val (xs, ys) = ([1], []) in merge (op >) (mergeSort (op >) xs, mergeSort (op >) ys) end
= merge (op >) (mergeSort (op >) [1], mergeSort (op >) [])
`- OOPS!
我不知道您是否注意到,但在我尝试计算 mergeSort (op >) [1] 时,我很快就被要求计算 mergeSort (op >) [1] 作为其结果的一部分。但是这样做我很快就会一次又一次地这样做。因此,从手动运行函数看来,无限递归位于我所说的 mergeSort 和您的代码所称的 real 中。
这是算法中的一个错误,可以通过说明单例列表的排序性来修复。
附带说明一下,我可能会完全重写这个函数:
fun merge cmp ([], ys) = ys
| merge cmp (xs, []) = xs
| merge cmp (xs as x::xs', ys as y::ys') =
case cmp (x, y) of
GREATER => y :: merge cmp (xs, ys')
| _ => x :: merge cmp (xs', ys)
fun sort cmp [] = []
| sort cmp [x] = [x]
| sort cmp xs =
let
val ys = List.take (xs, length xs div 2)
val zs = List.drop (xs, length xs div 2)
in
merge cmp (sort cmp ys, sort cmp zs)
end
fun down LESS = GREATER
| down GREATER = LESS
| down EQUAL = EQUAL
(我已经保留了这个错误。)
现在对整数进行排序:
sort (fn (x,y) => down (Int.compare (x,y))) [1,2,3]