Поскольку это проблема отладки, вот как я мог бы go найти эту проблему:
Сначала я запускаю программу и понимаю, что она вызывает исключение 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]
, являющаяся отправной точкой, заменяется определением mergeSort
на входе, который соответствует шаблону [1,2,3]
. - Каждая строка, начинающаяся с `- это моя попытка раскрыть часть выражения, не включив ее в общую перезапись вышеприведенного выражения - в противном случае это может стать немного грязным.
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]