gpt4 book ai didi

sorting - OCaml 中的尾递归合并排序

转载 作者:行者123 更新时间:2023-12-04 05:35:07 27 4
gpt4 key购买 nike

我正在尝试在 OCaml 中实现一个尾递归列表排序函数,我想出了以下代码:

let tailrec_merge_sort l =
let split l =
let rec _split source left right =
match source with
| [] -> (left, right)
| head :: tail -> _split tail right (head :: left)
in _split l [] []
in

let merge l1 l2 =
let rec _merge l1 l2 result =
match l1, l2 with
| [], [] -> result
| [], h :: t | h :: t, [] -> _merge [] t (h :: result)
| h1 :: t1, h2 :: t2 ->
if h1 < h2 then _merge t1 l2 (h1 :: result)
else _merge l1 t2 (h2 :: result)
in List.rev (_merge l1 l2 [])
in

let rec sort = function
| [] -> []
| [a] -> [a]
| list -> let left, right = split list in merge (sort left) (sort right)
in sort l
;;

然而,它似乎实际上并不是尾递归的,因为我遇到了“评估期间堆栈溢出(循环递归?)”错误。

你能帮我找出这段代码中的非尾递归调用吗?我已经搜索了很多,没有找到它。如果它是 sort 中的 let 绑定(bind)功能?

最佳答案

合并排序本质上不是尾递归的。 一个排序需要两次递归调用,并且在任何函数的任何执行中,最多一个动态调用可以在尾部位置。 ( split 也从非尾部位置调用,但因为它应该使用应该可以的常量堆栈空间)。

确保您使用的是最新版本的 OCaml。 在 3.08 及更早的版本中,List.rev可能会炸毁堆栈。此问题已在 3.10 版中修复。使用版本 3.10.2,我可以使用您的代码对一千万个元素的列表进行排序。这需要几分钟,但我不会破坏堆栈。所以我希望你的问题只是你有一个旧版本的 OCaml。

如果没有,下一步是设置环境变量

OCAMLRUNPARAM=b=1

当您炸毁堆栈时,它将给出堆栈跟踪。

我还想知道您正在排序的数组的长度;尽管合并排序不能是尾递归的,但它的非尾性质应该会花费您对数堆栈空间。

如果您需要对超过 1000 万个元素进行排序,也许您应该考虑使用不同的算法?或者,如果您愿意,您可以手动进行 CPS 转换合并排序,这将产生一个尾递归版本,代价是在堆上分配连续。但我的猜测是没有必要。

关于sorting - OCaml 中的尾递归合并排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2529580/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com