直接样式:
let rec quicksort list =
match list with
| [] -> []
| [element] -> [element]
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
let sorted_left = quicksort left in
let sorted_right = quicksort right in
sorted_left @ [pivot] @ sorted_right
我的第一次简单的翻译与Laurent的版本非常相似,只是排版有些不规则,以突出表明使用continuations的调用实际上是一种绑定:
let rec quicksort list cont =
match list with
| [] -> cont []
| element::[] -> cont [element]
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
quicksort left (fun sorted_left ->
quicksort right (fun sorted_right ->
cont (sorted_left @ [pivot] @ sorted_right)))
let qsort li = quicksort li (fun x -> x)
与Laurent相反,我发现检查cont
是否被遗忘很容易:从直接风格转换的CPS函数具有在每个分支中仅一次并且仅在尾部位置线性使用续体的属性。可以轻松检查没有忘记这样的调用。
但事实上,在大多数快速排序运行中(假设您不太倒霉或者首先对输入进行了洗牌,以获得大致对数行为),调用堆栈并不是问题,因为它仅以对数方式增长。更令人担忧的是频繁调用左参数为@
的函数,这是线性的。一种常见的优化技术是将函数定义为不返回列表而是“将输入添加到累加器列表”:
let rec quicksort list accu =
match list with
| [] -> accu
| element::[] -> element::accu
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
let sorted_right = quicksort right accu in
quicksort left (pivot :: sorted_right)
let qsort li = quicksort li []
当然,这可以再次转换为CPS:
let rec quicksort list accu cont =
match list with
| [] -> cont accu
| element::[] -> cont (element::accu)
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
quicksort right accu (fun sorted_right ->
quicksort left (pivot :: sorted_right) cont)
let qsort li = quicksort li [] (fun x -> x)
现在的一个最后的技巧是通过将它们转换为数据结构来“取消功能化”延续(假设分配数据结构比分配闭包略微更有效):
type 'a cont =
| Left of 'a list * 'a * 'a cont
| Return
let rec quicksort list accu cont =
match list with
| [] -> eval_cont cont accu
| element::[] -> eval_cont cont (element::accu)
| pivot::rest ->
let left, right = List.partition (fun element -> element < pivot) rest in
quicksort right accu (Left (left, pivot, cont))
and eval_cont = function
| Left (left, pivot, cont) ->
(fun sorted_right -> quicksort left (pivot :: sorted_right) cont)
| Return -> (fun x -> x)
let qsort li = quicksort li [] Return
最终,我选择了function .. fun
风格来定义eval_cont
函数,以突显它们只是CPS版本中的代码片段,但以下版本可能通过提高有效参数来实现更好的优化:
and eval_cont cont accu = match cont with
| Left (left, pivot, cont) ->
quicksort left (pivot :: accu) cont
| Return -> accu
list1 @ [x] @ list2
的速度较慢,请改用list1 @ x::list2
。 - Juliet