在 F#/OCaml 中实现类快速排序函数的尾递归版本

2023-11-23

是否可以实现快速排序算法的尾递归版本(通过延续模式)?如果是的话,将如何实施?

普通(未优化)版本:

let rec quicksort list =
 match list with
 | [] -> []
 | element::[] -> [element]
 | pivot::rest -> let ``elements smaller than pivot``, ``elements larger or equal to pivot``= 
                    rest |> List.partition(fun element -> element < pivot)
                  quicksort ``elements smaller than pivot`` @ [pivot] @ quicksort ``elements larger or equal to pivot``

直接风格:

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 的版本非常相似,只是缩进有点奇怪,以表明带有延续的调用实际上是一种绑定:

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)

与洛朗相反,我发现很容易检查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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 F#/OCaml 中实现类快速排序函数的尾递归版本 的相关文章

  • Haskell 对于 Web 应用程序来说足够成熟吗? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 使用fold_left/right反转OCaml中的列表

    更新 解决方案 感谢 jacobm 的帮助 我想出了一个解决方案 Folding Recursion let reverse list 3 theList List fold left fun element recursive call
  • 如何在不改变也不重新分配的情况下实现可设置和可检索的状态?

    编写代码时可以遵循以下几条规则 当没有重新分配时 代码更容易阅读和推理 许多 linter 推荐首选const只要有可能 代码也更容易阅读和推理对象何时不会发生变化 如果您在代码的一部分中定义了一个对象 那么知道您可以在其他地方自由引用该对
  • 将事件绑定到 ItemsControl 中的按钮

    我有一个 Windows Phone 7 应用程序 其中包含一些 xaml 如下所示
  • gfortran 支持尾调用消除吗?

    我编写了这个小程序来测试 gfortran 是否执行尾调用消除 program tailrec implicit none print tailrecsum 5 0 contains recursive function tailrecsu
  • 如何从引用的表达式匹配中获取模块、函数等的 F# 名称

    我继续开发 F 引用表达式的打印机 它不一定是完美的 但我想看看有什么可能 中的活跃模式Microsoft FSharp Quotations Patterns and Microsoft FSharp Quotations Derived
  • 带表达式的 F# 类型定义

    是否可以这样表达 type id int gt 0 我知道它不可能静态执行 因为这意味着 F 具有依赖类型 在 C 中 我习惯于使用代码契约来执行此类操作并获得运行时强制执行 我正在这里寻找类似的东西 Thanks 编辑 感谢您提供的所有答
  • F# 中的动态编程

    实现解决问题的动态规划算法的最优雅的方法是什么子问题重叠的问题 http en wikipedia org wiki Overlapping subproblem 在命令式编程中 人们通常会创建一个按问题大小索引的数组 至少在一维 然后算法
  • 有没有好的 Clojure 基准测试?

    Edit Clojure 基准测试已达到基准游戏 http benchmarksgame alioth debian org u64q clojure html 我已经制作了这个问题社区维基并邀请其他人保持更新 有人知道 Clojure 性
  • 为什么在 OCaml 中更喜欢柯里化而不是元组参数?

    Caml简介 http www cs jhu edu scott pl lectures caml intro html says 请注意 在 Caml 中 最好对多参数函数使用柯里化函数定义 而不是元组 比较时 a gt b gt c调用
  • F# 使用 while 循环

    我有一个数据读取器 我想从中返回行集合 在阅读了一天的书籍后 我无法找到在 f 中执行此操作的最佳方法 我可以在 F 中以正常的 C 方式进行操作 但这不是我使用 f 的原因 这就是我想要实现的目标 let values while rea
  • 为什么计算斐波那契数需要很长时间?

    几天前我开始学习Ocaml 我尝试编写一个斐波那契数字程序 let rec fib a if a 1 a 2 then 1 else fib a 1 fib a 2 该代码不是最佳的 因为我不知道如何处理异常情况 但现在 如果我尝试计算 f
  • 合并具有公共字段的列表的最快方法?

    我正在学习 F 并且正在做赔率比较服务 ala www bestbetting com 以将理论付诸实践 到目前为止 我有以下数据结构 type price Bookie string Odds float32 type selection
  • 从函数返回随机值是副作用吗?

    我当时正在编写一些 F 代码 并且正在编写一个从一组字符串中返回随机字符串的函数 假设我有这样的事情 open System let a a b c d let rstring arr string let r new Random arr
  • 使用 FParsec 解析 int 或 float

    我正在尝试使用 FParsec 解析文件 该文件由 float 或 int 值组成 我面临两个问题 无法找到好的解决方案 1 Both pint32 and pfloat将成功解析相同的字符串 但给出不同的答案 例如pint32将返回3解析
  • Async.AwaitTask 在 f# 中如何工作?

    我知道 f 和 c 异步模型之间的主要区别在于 在 f 中 除非您调用 Async RunSynchronously 之类的内容 否则异步执行不会开始 在 C 中 当方法返回任务时 通常 并非总是 立即在后台线程中开始执行 Async Aw
  • Scala 功能设计模式目录

    一周以来我一直在阅读 Scala 编程 作者一步一步地介绍了该语言的元素 但我仍然很困惑何时使用演员 闭包 柯里化等功能性的东西 我正在寻找功能结构的典型用例或最佳实践的目录 我并不是说在 Scala 中重新实现像 GoF 这样的众所周知的
  • Java泛型 - 实现像map这样的高阶函数

    我决定用 Java 编写一些常见的高阶函数 map filter reduce 等 这些函数通过泛型实现类型安全 但我在一个特定函数中遇到通配符匹配问题 为了完整起见 函子接口是这样的 The interface containing th
  • 从静态成员访问 let 绑定字段

    有没有办法从静态成员访问 let 绑定字段 下面给出了指示的错误 type Foo x let x x static member test let foo Foo System DateTime Now Month printfn A f
  • 需要澄清令人困惑的 Http4s 消息类型 `Response[F]` / `Request[F]`

    我很难理解为什么Request and Response参数化为F 类似的东西是猫效应数据类型资源 从文档中 https typelevel org cats effect docs std resource https typelevel

随机推荐

  • 非阻塞获取字符

    平台 Linux 3 2 0 x86 Debian 7 编译器 GCC 4 7 2 Debian 4 7 2 5 我正在编写一个函数 如果标准输入中已存在字符 则从标准输入读取单个字符 如果 stdin 为空 则该函数将不执行任何操作并返回
  • 如何使用 JPA/Hibernate 设置复合主键的列顺序

    我在组合主键中的列排序时遇到问题 我有一个包含以下内容的表 Embeddable public class MessageInfo implements Serializable private byte loc private long
  • Django 脆皮表单、BaseGenericInlineFormSet 和allow_delete

    我在使用 django crispy forms 时遇到了一个问题 我无法得到答案 我有一个相当复杂的表单布局 到目前为止 一切都与 cripy forms 一起工作得非常好 表单的一部分使用通用内联表单集 这也有效 但我的问题是 我无法弄
  • 如何更改TabControl中选定选项卡的颜色?

    我正在实施一个TabControl用于 WPF 中的对话框 所选选项卡 鼠标按下 的颜色默认为白色 我想将所选选项卡的颜色更改为悬停的颜色 当我将鼠标悬停在选项卡上时 选项卡的颜色更改为 Office blue gradient 这就是我想
  • 当行包含特定文本时计算行数

    可能是一个简单的问题 但我找不到简单的答案 我们以数据框 df1 中的以下列 Status 为例 Status Planned Unplanned Missing Corrected 我想计算单元格包含 计划 和 缺失 时的行数 我尝试了以
  • 在谷歌地图反向地理编码中获取明确的城市名称

    使用 Google 地图地理编码 API 我能够获取特定坐标的格式化地址 为了获得确切的城市名称 我正在执行以下操作 ajax url http maps googleapis com maps api geocode json latln
  • 在 PHP 中模拟 LIKE

    有没有办法用相同的语法在PHP中模拟SQL的LIKE运算符 and 通配符和泛型 escape转义字符 这样就有 value LIKE string ESCAPE escape 你可以有一个函数 在不使用数据库的情况下返回 PHP 评估吗
  • 如何在Python中获取/设置逻辑目录路径

    在 python 中 是否可以获取或设置逻辑目录 而不是绝对目录 例如 如果我有 real path to dir 我有 linked path to dir 链接到同一目录 使用 os getcwd 和 os chdir 将始终使用绝对路
  • 如何在下载真实图像之前显示占位符图像?

    这个想法是在下载真正的高分辨率图像之前显示图像的低分辨率版本 最好使用 img 标签 img 低分辨率图像首先可见 下载后将替换为高分辨率图像 如何才能做到这一点 是否可以编辑 img src 属性 或者应该创建其他内容 例如带背景的包装
  • 分析 C++ 多线程应用程序

    您是否使用过诸如 Intel Vtune 分析器之类的分析工具 您对 Linux 和 Windows 上的 C 多线程应用程序有何建议 我主要对缓存未命中 内存使用 内存泄漏和 CPU 使用感兴趣 我使用 valgrind 仅在 UNIX
  • angular-i18n Angular 6 国际化:如何处理变量

    我已经在这里阅读了整个文档 https angular io guide i18n 我无法弄清楚我应该如何处理这种性质的 html 标签 div class title text currentPage div 或者这样的 div clas
  • Haskell IO 测试

    我一直试图弄清楚 Haskell 中是否已经有一种可接受的测试文件 io 操作的方法 但我还没有找到任何对我想做的事情有用的信息 我正在编写一个执行各种文件系统操作的小型库 递归遍历目录并返回所有文件的列表 同步多个目录 以便每个目录包含使
  • 使用“同名”属性实现 2 个接口

    这似乎是一个合理的 也许很简单 场景 但是您将如何执行以下操作 假设我有 2 个接口 Interface ISimpleInterface string ErrorMsg get End Interface Interface IExten
  • 从 Google Cloud 流式传输视频

    我正在考虑为我的项目使用 Google App Engine 和 Google Cloud Platform 而不是使用 Amazon AWS 我需要能够大量流式传输视频 并在需要时快速扩展 App Engine 看起来非常适合负载平衡 扩
  • 如何在多线程之间共享一笔事务

    我们遇到了一个使用多线程的场景 在主线程中 执行一些逻辑并更新数据库 在某个时刻 它会调用另一个服务来更新数据库 该服务在另一个线程中运行 我们希望两个线程共享同一个事务 也就是说 任何一个线程中的操作失败 那么另一个线程中的操作也会被回滚
  • 是否有关于在 iPhone 上的 openGL ES 中加载 3D 模型的教程?

    不久前我开始接触一些 3D 建模者 现在我很好奇 如何将这样的 3D 模型带到 iPhone 或 iPad 上 以便我可以在屏幕上看到它 甚至可以通过手势旋转它 1 3D 模型的最佳文件格式是什么 2 如何将特定的 3D 模型文件加载到 o
  • 路由测试 ASP.NET MVC4

    我一直在使用 MvcRouteUnitTester codeplex and nuget 跑步自动化单元测试反对我的路线 体验一下它的功能 assert incoming route tester WithIncomingRequest F
  • gradle“构建”任务混乱

    您好 我有多项目 gradle 设置 root project sub project1 sub project2 sub project3 一切都很好 但有一件事让我发疯 在我的构建脚本中 defaultTasks build lt th
  • 保存方向变化时的片段状态

    我正在为 Android 创建一个文件管理器应用程序 下面的两个类是执行此操作的主要逻辑 我正在做的是 在 ContentList 启动时 它将 ContentListFragment 添加到 ContestList xml 中的容器布局中
  • 在 F#/OCaml 中实现类快速排序函数的尾递归版本

    是否可以实现快速排序算法的尾递归版本 通过延续模式 如果是的话 将如何实施 普通 未优化 版本 let rec quicksort list match list with gt element gt element pivot rest