SML:递归调用快速排序时的值限制错误

问题描述

我正在为练习编写一个快速排序函数。我已经知道 5 行函数快速排序;但我想通过让它扫描列表一次并返回一对将原始列表分成两半的列表来改进分区。所以我写道:

fun partition nil = (nil,nil)
  | partition (pivot :: rest) =
  let
    fun part (lst,pivot,(lesseq,greater)) =
      case lst of
           [] => (lesseq,greater)
         | (h::t) =>
             if h <= pivot then part (t,(h :: lesseq,greater))
             else part (t,h :: greater))
  in
    part (rest,([pivot],[]))
  end;

这个分区足够好。它给了我一个签名val partition = fn : int list -> int list * int list。它按预期运行。

当我使用下面的快速排序时,事情开始破裂。

fun quicksort_2 nil = nil
  | quicksort_2 lst = 
let
  val (lesseq,greater) = partition lst
in
  quicksort_2 lesseq @ quicksort_2 greater
end;

如果消除对 quicksort_2 的递归调用,我可以运行上述函数;但是如果我把它们放回去(实际上去整理东西),它就会停止运行。签名也会不正确,给我 val quicksort_2 = fn : int list -> 'a list。我在列表上调用函数时收到的警告是:

Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...)

这里有什么问题?我没有使用任何 ref 变量;我尝试过的类型注释似乎没有帮助...

解决方法

主要问题是您的快速排序功能缺少单例列表基本情况。应该是

fun quicksort [ ] = [ ]
  | quicksort [x] = [x]
  | quicksort xs  = 
      let
        val (l,r) = partition xs
      in
        quicksort l @ quicksort r
      end

然后应该有类型 int list -> int list 给定您的 partition 的类型。我们必须添加这种情况,否则您将永远不会遇到基本情况,而是无限期地递归。


有关您为什么会遇到问题的更多详细信息:

签名也不正确,给我val quicksort_2 = fn : int list -> 'a list

这是因为您的函数的codomain 从未被限制为比'a list 更不通用。查看原始实现中可能的分支,我们可以看到在 nil 分支中您返回 nil(最通用的类​​型 'a list),并且在递归情况下您得到两个 { {1}}s(根据我们目前的假设)并附加它们,产生一个 'a list---这很好,因此您的类型不会受到进一步限制。

[值限制警告] 这里有什么问题?我没有使用任何 'a list 变量

值限制实际上与 ref 无关(尽管在使用它们时经常会出现)。取而代之的是禁止任何在顶层的多态性的东西必须是其语法的值(因此排除了计算在顶层类型抽象器后面的可能性)。这是因为给定 ref 我们(忽略值限制)有 xs : int list ---否则它将是多态的,但不是语法值。相应地它是值限制的。