Recursion 用函数式语言实现快速排序

Recursion 用函数式语言实现快速排序,recursion,functional-programming,sml,tail-recursion,ml,Recursion,Functional Programming,Sml,Tail Recursion,Ml,我需要在SML中为家庭作业实施快速排序,我迷路了。我以前不熟悉quicksort是如何实现的,所以我仔细阅读了这篇文章,但我读到的每一个实现都是必不可少的。这些看起来不太难,但我不知道如何在功能上实现快速排序 维基百科碰巧有标准ML格式的快速排序代码(这是我的作业所需的语言),但我不明白它是如何工作的 维基百科代码: val filt = List.filter fun quicksort << xs = let fun qs [] = [] | qs [x] = [x] | q

我需要在SML中为家庭作业实施快速排序,我迷路了。我以前不熟悉quicksort是如何实现的,所以我仔细阅读了这篇文章,但我读到的每一个实现都是必不可少的。这些看起来不太难,但我不知道如何在功能上实现快速排序

维基百科碰巧有标准ML格式的快速排序代码(这是我的作业所需的语言),但我不明白它是如何工作的

维基百科代码:

val filt = List.filter
fun quicksort << xs = let
fun qs [] = []
 | qs [x] = [x]
 | qs (p::xs) = let
     val lessThanP = (fn x => << (x, p))
     in
       qs (filt lessThanP xs) @ p :: (qs (filt (not o lessThanP) xs))
     end
in
  qs xs
end
其中分区定义为

// left is the index of the leftmost element of the array
// right is the index of the rightmost element of the array (inclusive)
//   number of elements in subarray = right-left+1
function partition(array, 'left', 'right', 'pivotIndex')
  'pivotValue' := array['pivotIndex']
  swap array['pivotIndex'] and array['right']  // Move pivot to end
  'storeIndex' := 'left'
  for 'i' from 'left' to 'right' - 1  // left ≤ i < right
      if array['i'] < 'pivotValue'
          swap array['i'] and array['storeIndex']
          'storeIndex' := 'storeIndex' + 1
  swap array['storeIndex'] and array['right']  // Move pivot to its final place
  return 'storeIndex'
//left是数组最左边元素的索引
//right是数组最右边元素的索引(包括)
//子阵列中的元素数=左+右+1
函数分区(数组、“左”、“右”、“数据透视索引”)
“pivotValue”:=数组['pivotIndex']
交换数组['pivotIndex']和数组['right']//将数据透视移动到末尾
'storeIndex':='left'
对于“i”,从“左”到“右”-1//左≤ 我是对的
如果数组['i']<'pivotValue'
交换数组['i']和数组['storeIndex']
'storeIndex':='storeIndex'+1
交换数组['storeIndex']和数组['right']//将轴移动到其最终位置
返回“storeIndex”
那么,分区到底发生在哪里呢?还是我对SMLs快速排序的想法是错误的?

您说过:

filt将返回小于p*的xs中所有内容的列表,该列表与p连接,p连接到所有内容>=p*

这不太准确
filt
将返回小于
p
的xs中所有内容的列表,但新列表不会立即与
p
连接。事实上,新列表被递归地传递给
qs
,而
qs
返回的任何内容都与
p
连接在一起

在伪代码版本中,分区在
数组
变量中发生。这就是为什么在
分区
循环中会看到
交换
。在适当的位置执行分区比创建副本更能提高性能

那么,分区到底发生在哪里呢?还是我错误地认为SMLs快速排序

quicksort的纯功能实现通过输入列表上的结构递归工作(IMO,这一点值得一提)。此外,正如您所看到的,对“filt”的两个调用允许您将输入列表划分为两个子列表(例如A和B),然后可以分别处理这两个子列表。重要的是:

  • A的所有元素都小于或等于枢轴元素(代码中的“p”)
  • B的所有元素都大于枢轴元素
通过交换同一数组中的元素,命令式实现可以在适当的位置工作。在您提供的伪代码中,“partition”函数的后不变量是有两个子数组,一个子数组从输入数组的“left”开始(以“pivotIndex”结束),另一个子数组从“pivotIndex”之后开始,以“right”结束。这里重要的是,这两个子阵列可以看作是子列表A和B的表示


我想现在你已经知道分区步骤发生在哪里了(或者反过来说,命令和功能是如何关联的)。

感谢你澄清了我的误解。另外,我对SML代码中的分区发生在哪里感到困惑,而不是伪代码。抱歉,如果不清楚的话。@rob,@nate:“…filt将返回一个xs中小于p*的所有内容的列表,该列表与p连接,p连接到所有内容>=p.*”事实上,这不太正确。隐式括号是这样的:qs(filt-lessThanP-xs)@
p::(qs(filt(not o-lessThanP)xs))
。不能将列表压缩到列表中,只能压缩单个元素。列表相互附加(@)。
// left is the index of the leftmost element of the array
// right is the index of the rightmost element of the array (inclusive)
//   number of elements in subarray = right-left+1
function partition(array, 'left', 'right', 'pivotIndex')
  'pivotValue' := array['pivotIndex']
  swap array['pivotIndex'] and array['right']  // Move pivot to end
  'storeIndex' := 'left'
  for 'i' from 'left' to 'right' - 1  // left ≤ i < right
      if array['i'] < 'pivotValue'
          swap array['i'] and array['storeIndex']
          'storeIndex' := 'storeIndex' + 1
  swap array['storeIndex'] and array['right']  // Move pivot to its final place
  return 'storeIndex'