Ocaml 中查找树深度的尾递归函数

2023-12-20

我有一个类型tree定义如下

type 'a tree = Leaf of 'a | Node of 'a * 'a tree * 'a tree ;;

我有一个函数可以找到树的深度,如下所示

let rec depth = function 
    | Leaf x -> 0
    | Node(_,left,right) -> 1 + (max (depth left) (depth right))
;;

该函数不是尾递归。有没有办法让我以尾递归的方式编写这个函数?


您可以通过将函数转换为 CPS(连续传递风格)来简单地做到这一点。这个想法是,而不是打电话depth left,然后根据这个结果进行计算,你可以调用depth left (fun dleft -> ...),其中第二个参数是“一旦结果(dleft)可用”。

let depth tree =
  let rec depth tree k = match tree with
    | Leaf x -> k 0
    | Node(_,left,right) ->
      depth left (fun dleft ->
        depth right (fun dright ->
          k (1 + (max dleft dright))))
  in depth tree (fun d -> d)

这是一个众所周知的技巧,可以使任何函数成为尾递归。瞧,这是尾部记录。

下一个众所周知的技巧是“去功能化”CPS 结果。延续的表示((fun dleft -> ...)部分)作为函数很简洁,但您可能想看看它作为数据是什么样子。因此,我们用数据类型的具体构造函数替换每个闭包,该构造函数捕获其中使用的自由变量。

这里我们有三个延续闭包:(fun dleft -> depth right (fun dright -> k ...)),仅重用环境变量right and k, (fun dright -> ...),它重用了k以及现在可用的左结果dleft, and (fun d -> d),初始计算,没有捕获任何内容。

type ('a, 'b) cont =
  | Kleft of 'a tree * ('a, 'b) cont (* right and k *)
  | Kright of 'b * ('a, 'b) cont     (* dleft and k *)
  | Kid

解函函数如下所示:

let depth tree =
  let rec depth tree k = match tree with
    | Leaf x -> eval k 0
    | Node(_,left,right) ->
      depth left (Kleft(right, k))
  and eval k d = match k with
    | Kleft(right, k) ->
      depth right (Kright(d, k))
    | Kright(dleft, k) ->
      eval k (1 + max d dleft)
    | Kid -> d
  in depth tree Kid
;;

而不是构建一个函数k并将其涂在叶子上(k 0),我构建了一个类型的数据('a, int) cont,需要稍后eval用来计算结果。eval,当它通过Kleft,执行闭包操作(fun dleft -> ...)正在做的,那就是递归调用depth在右子树上。eval and depth是相互递归的。

现在仔细看看('a, 'b) cont,这个数据类型是什么?这是一个清单!

type ('a, 'b) next_item =
  | Kleft of 'a tree
  | Kright of 'b

type ('a, 'b) cont = ('a, 'b) next_item list

let depth tree =
  let rec depth tree k = match tree with
    | Leaf x -> eval k 0
    | Node(_,left,right) ->
      depth left (Kleft(right) :: k)
  and eval k d = match k with
    | Kleft(right) :: k ->
      depth right (Kright(d) :: k)
    | Kright(dleft) :: k ->
      eval k (1 + max d dleft)
    | [] -> d
  in depth tree []
;;

列表就是一个堆栈。我们这里实际上是前一个递归函数的调用堆栈的具体化(转换为数据),两种不同的情况对应于两种不同类型的非 tailrec 调用。

请注意,取消功能只是为了好玩。实际上,CPS 版本很短,很容易手动导出,也很容易阅读,我建议使用它。闭包必须在内存中分配,但元素也是如此('a, 'b) cont-- 尽管这些可能会更紧凑地表示。我会坚持使用 CPS 版本,除非有充分的理由去做一些更复杂的事情。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Ocaml 中查找树深度的尾递归函数 的相关文章

  • 如何将函数转换为点自由形式?

    假设我有一个 JavaScript 函数 function f x return a b x c x 我如何将其转换为无点函数 通过组合函数 还有关于这方面的更多信息的资源吗 一般来说 当您将函数转变为无点风格时 没有简单的规则可遵循 要么
  • 如何在 Haskell 中枚举递归数据类型?

    这篇博文 http lukepalmer wordpress com 2008 05 02 enumerating a context free language 对于如何使用 Omega monad 对角枚举任意语法有一个有趣的解释 他提
  • 何时使用接口,何时使用高阶函数?

    给定一个具有以下层的 ASP NET MVC 应用程序 UI 视图 CSS Javascript 等 控制器 服务 包含业务逻辑和数据访问 没有单独的数据访问层的原因是我正在使用 SQL 类型提供程序 以下代码可能不起作用 因为它只是原始草
  • 通过命令行参数选择要使用的 ocaml 模块

    在我的代码中我有module M Implementation1然后我参考M 代替Implementation1 问题是 我必须重新编译我的程序才能改变Implementation1 to Implementation2 我想通过命令行参数
  • javascript中的父子关系排序

    我有以下结构 category id 1 parent category null category id 2 parent category 1 category id 3 parent category 1 category id 4
  • 字符串 c 的二叉树

    我正在尝试实现一个能够在 c 中保存字符串的二叉树 在让代码适用于整数之后 我尝试稍微修改它以处理字符数组 现在我似乎完全破解了代码 但不知道如何破解 任何帮助表示赞赏 include
  • 类型级编程有哪些示例? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我不明白 类型级编程 是什么意思 也无法使用Google找到合适的解释 有人可以提供一个演示类型级编程的示例吗 范式的解释和 或定义将
  • 将名称绑定到值与将值分配给变量

    阅读 Bartosz Milewski 的文章完整的 https www fpcomplete com school starting with haskell basics of haskell 3 pure functions lazi
  • 如何在Java 8中实现Elvis运算符?

    我有一个经典的 Elvis 运算符 案例 其中我调用每个可能返回 null 的方法并将它们链接在一起 thing nullableMethod1 a nullableMethod2 b nullableMethod3 在 Java 8 中
  • 是否有适用于 Haskell 或 Scala 等函数式语言的 LL 解析器生成器?

    我注意到明显缺乏用函数式语言创建解析器的 LL 解析器 我一直在寻找但没有成功的理想发现是为 ANTLR 风格的 LL 语法生成 Haskell 解析器 语法的模小数重新格式化 并且令我惊讶的是 每个最后一个解析器生成器都具有函数我发现的语
  • Scala:解决“非法循环引用”

    我正在尝试实现一个基于 HashMap 的树 它支持给定根键的 O 1 子树查找 为了实现这个目标 我正在努力做到以下几点 scala gt type Q HashMap Char Q
  • Javascript 与 Python 关于 Python 'map()' 函数的比较

    Python中有一个函数叫做map这可以让你去 map someFunction x y z 并继续应用该功能的列表 是否有与此功能等效的 JavaScript 我现在刚刚学习Python 虽然我被告知javascript是函数式语言 但我
  • 将图的 BFS 代码转换为 DFS 代码

    如果这个问题听起来模棱两可 我很抱歉 但我在采访中被问到了这个问题 为图 树中的 BFS 编写一个程序 我使用队列编写了流行的代码 现在他要求我通过修改我刚刚编写的 BFS 代码的一行来将其转换为 DFS 代码 我能想到的唯一答案是使用堆栈
  • Javascript 作为一种函数式语言

    我正在寻求掌握函数式编程概念 我多年来一直在 Web 应用程序中使用 Javascript 进行客户端脚本编写 除了使用原型之外 它都是简单的 DOM 操作 输入验证等 最近 我有经常阅读 http eloquentjavascript n
  • 单击 jstree 节点,以该节点为根重建树

    我认为这个主题相当明确 我是 jstree 新手 并尝试解析文档 但我得到了 有点被这个问题困扰了 我有以下代码 tree jstree json data data tree company themes theme smb dots f
  • Coq:Type(n) 中的 Prop 与 Set

    我想考虑以下三个 相关的 Coq 定义 Inductive nat1 Prop z1 nat1 s1 nat1 gt nat1 Inductive nat2 Set z2 nat2 s2 nat2 gt nat2 Inductive nat
  • 将数组初始化为空白自定义类型 OCAML

    我设置了自定义数据类型 type vector a float b float 我想初始化一个向量类型的数组 但不包含任何内容 只是一个长度为 x 的空数组 下列 let vecarr Array create max seq length
  • 在函数式编程中画UML类图有什么意义吗?

    我被要求在一个学校项目中展示UML我使用的图表 如果我这样做的话 实现该项目 但我正在做的项目是用 C 语言编写的 并且已经进行了功能编程 因此 我想证明 在不使用面向对象语言的情况下使用类图是没有意义的 但我担心这不是真的 并且无法证实这
  • Python 的“with”是一元吗?

    像我之前的许多鲁莽的先驱者一样 我正在努力穿越理解单子这片无路可走的荒原 我仍然在蹒跚学步 但我不禁注意到 Python 的某种类似 monad 的品质with陈述 考虑这个片段 with open input filename r as
  • 如何编写将布尔值返回到一个函数的函数

    我在这里发现了一个类似的问题 它问了几乎相同的问题 但又不完全一样 我的问题是如何将 a gt Bool 类型的函数列表组合成一个也是 a gt Bool 的函数 Ex compose a gt Bool gt a gt Bool comp

随机推荐