【问题标题】:Why does this SML mergeSort function not return a sorted list?为什么这个 SML mergeSort 函数不返回排序列表?
【发布时间】:2020-02-27 18:25:50
【问题描述】:

它最终什么也没返回。此外,在运行时,它说它是:

- val merge_sort = fn : ('a * 'a -> bool) -> 'b list -> 'a list

当我知道应该是这样的时候:

- val merge_sort = fn : ('a * 'a -> bool) -> 'a list -> 'a list

功能:

fun merge_sort f = 
    let
        fun merge(nil, ylist) = ylist
        |   merge(xlist, nil) = xlist
        |   merge(x::xend, y::yend) =
        if f (x,y) then
            x::merge(xend, y::yend)
        else
            y::merge(x::xend, yend)

        fun split nil = (nil, nil) 
        |   split [x] = ([x], nil)
        |   split (x::y::xy) = 
            let 
                val (low, up) = split xy
            in 
                (x::low, y::up)
            end

    in
        let
            fun real nil = nil
            | real L = 
                let
                    val (list1,list2) = split L
                in
                    merge (real list1,real list2)
            end
        in
             fn last => real last
        end
    end;

 merge_sort (op >) [0, 5, 1, ~4, 9, 11]

【问题讨论】:

  • 我不会投反对票,因为没有尝试调试,但我鼓励您阅读idownvotedbecau.se/nodebugging - 特别是,“调试代码是开发人员可以做的最重要的事情之一当程序的结果出乎意料,或者程序在运行时崩溃时,调试是开发人员快速隔离和识别问题的一种方式,绝大多数情况下,一旦发现问题,解决方案就很清楚了。但是,如果开发人员不调试他们的代码,问题就会变得更难解决。”
  • 将您的解决方案与 Q&A Standard sorting functions in SML? 中提供的解决方案进行比较应该是除调试之外的另一种检测错误的方法,如下面的答案中建议的那样。

标签: mergesort sml


【解决方案1】:

有趣的类型实际上与使你的函数永远不会终止的错误有点相关。

删除自定义比较并分离助手(并折叠 merge_sortreal):

fun split nil = (nil, nil) 
  | split [x] = ([x], nil)
  | split (x::y::xy) = 
    let 
        val (low, up) = split xy
    in 
        (x::low, y::up)
    end;

fun merge (nil, ylist) = ylist
  | merge (xlist, nil) = xlist
  | merge (x::xend, y::yend) =
    if x < y then
        x::merge (xend, y::yend)
    else
        y::merge (x::xend, yend);


fun merge_sort nil = nil
  | merge_sort L =
    let
        val (list1,list2) = split L
    in
        merge (merge_sort list1, merge_sort list2)
    end;

我们得到这些类型:

val split = fn : 'a list -> 'a list * 'a list
val merge = fn : int list * int list -> int list
val merge_sort = fn : 'a list -> int list

merge_sort 获取任何内容的列表并生成int list
这很奇怪。
让我们看看它是如何得出的。

fun merge_sort nil = nil

nil 可以是任何内容的列表,因此得到'a list -&gt; 'a list

| merge_sort L =
  let
      val (list1,list2) = split L
  in
      merge (merge_sort list1, merge_sort list2)
  end;

现在,结果一定是int list,因为这是merge产生的结果,也和merge的参数一致。
但是仍然没有办法从merge_sort 的参数中推断出更具体的类型——它只传递回merge_sort,而'a list 是我们已经得到的,所以我们最终得到'a list -&gt; int list

看看当你对单例列表进行排序时会发生什么:

    merge-sort [1]
--> let val (list1, list2) = split [1] in merge (merge_sort list1, merge_sort list2)
--> merge (merge_sort [1], merge_sort [])

我们有一个不会终止的递归。

你需要一个单独的单例列表的基本情况:

| merge_sort [x] = [x]

当你添加它时,类型就是它们应该是的。

【讨论】:

  • 感谢您的详细回复!你是对的,这是“真实”模糊性的问题。
【解决方案2】:

由于这是一个调试问题,我可以通过以下方式找到问题:

首先,我运行程序并意识到它引发了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 &gt;) [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 &gt;) [1] 时,我很快就被要求计算 mergeSort (op &gt;) [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]

【讨论】:

    猜你喜欢
    • 2019-11-23
    • 1970-01-01
    • 2020-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    相关资源
    最近更新 更多