Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/38.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 带尾部递归的树到有序列表_Recursion_Ocaml_Tail Recursion - Fatal编程技术网

Recursion 带尾部递归的树到有序列表

Recursion 带尾部递归的树到有序列表,recursion,ocaml,tail-recursion,Recursion,Ocaml,Tail Recursion,事实上,我在一个问题上坐了一个多小时,却没有找到解决办法。 我有以下数据类型: 键入'a tree=Empty |'a*'树的节点*a树 我必须找到一个函数,它可以转换一个有序列表中的给定树。也没有像左子对象必须小于右子对象这样的不变量。我已经找到了一个“正常”的递归解决方案,但不是尾部递归解决方案。我已经考虑过构建一个无序列表,并使用list.sort对其进行排序,但这使用了一种不是尾部递归的合并排序。也许有人有个好建议 谢谢大家! 如果您希望按顺序遍历树并返回列表,这意味着我们的函数在ord

事实上,我在一个问题上坐了一个多小时,却没有找到解决办法。 我有以下数据类型:
键入'a tree=Empty |'a*'树的节点*a树

我必须找到一个函数,它可以转换一个有序列表中的给定树。也没有像左子对象必须小于右子对象这样的不变量。我已经找到了一个“正常”的递归解决方案,但不是尾部递归解决方案。我已经考虑过构建一个无序列表,并使用
list.sort
对其进行排序,但这使用了一种不是尾部递归的合并排序。也许有人有个好建议


谢谢大家!

如果您希望按顺序遍历树并返回列表,这意味着我们的函数
在order
中必须具有类型
'a tree->'a list

let rec inorder t =
    match t with
      | Empty -> []
      | Node (v, l, r) -> List.append (inorder l) (v :: (inorder r)) (* ! *)
但是,
List.append
位于尾部位置,而不是
inoder
。另一个问题是我们有两个调用
的顺序
。如果我们把
inorder l
放在尾部位置,
inorder
不可能在尾部位置,反之亦然

解决这个问题的一个好方法是继续传球。我们使用上面的函数,并将其转换为一个辅助函数,该函数带有一个额外的参数,用于继续,
return

(* convert to helper function, add an extra parameter *)
let rec loop t return =
    match t with
        | Empty -> ...
        | Node (v, l, r) -> ...
continuation表示“下一步要做什么”,因此我们必须将值直接发送到continuation,而不是从函数中发送。这意味着对于
Empty
案例,我们将
返回[]
——而不是简单地
[]

let rec loop t return =
    match t with
        | Empty -> return []
        | Node (v, l, r) -> ...
对于
节点(v,l,r)
的情况,现在我们有了一个额外的参数,我们可以编写自己的延续,通知
循环
接下来要做什么。因此,为了构建我们的排序列表,我们需要
循环l
,然后
循环r
(反之亦然),然后我们可以
附加它们。我们将这样编写程序

let rec loop t return =
    match t with
        | Empty -> return []
        | Node (v, l, r) ->
            loop l ... (* build the left_result *)
            loop r ... (* build the right_result *)
            return (List.append left_result (v :: right_result))
在下一步中,我们将填写continuations的实际lambda语法

let rec loop t return =
    match t with
        | Empty -> return []
        | Node (v, l, r) ->
            loop l (fun left ->
            loop r (fun right ->
            return (List.append left (v :: right))))
最后,我们定义了
inorder
,它是对
loop
的调用,带有默认的延续,
identity

let identity x =
    x

let inorder t =
    let rec loop t return =
        match t with
            | Empty -> return []
            | Node (v, l, r) ->
                loop r (fun right ->
                loop l (fun left ->
                return (List.append left (v :: right))))
    in
    loop t identity
正如您所看到的,
循环r(有趣的右->…)
位于
节点的尾部位置<代码>循环l(左->…)
位于第一个延续的尾部位置。并且
List.append…
位于第二个延续的尾部位置。提供的
List.append
是一个尾部递归过程,
inorder
不会增加堆栈



注意:对于大树来说,使用
List.append
可能是一个代价高昂的选择。我们的函数为每个
节点调用它一次。你能想出一个办法来避免它吗?这个练习留给读者。

听起来像是一个作业。需要考虑的一点是,任何代码都可以使用“延续传递样式”转换为尾部递归代码。将代码转换为这种风格需要一个机械的过程。如果您阅读了有关转换的内容,您可能可以手动将其应用到代码中。这就是我要开始的地方。有趣的是:RosettaCode-OCaml哇,太棒了!没想到这么容易。我已经实现了一个非常“脏”的版本,有一个额外的堆栈来存储父母等等。但是这个版本非常好!