我实际上正坐在一个多小时的问题上,却没有找到解决方案。
我有这种数据类型:
type 'a tree = Empty | Node of 'a * 'a tree * 'a tree
我必须找到一个函数,该函数可以在有序列表中转换给定的树。也没有不变性,就像左边的孩子必须小于右边的孩子。我已经找到了“常规”递归解决方案,但没有找到尾递归解决方案。我已经考虑过要建立一个无序列表,并用List.sort
对其进行排序,但是这使用了不是尾递归的合并排序。也许有人有很好的建议。
谢谢!
答案 0 :(得分:3)
如果要依次遍历树 并返回 list ,则意味着我们的函数inorder
必须具有类型'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
处于尾部位置,而不是inorder
。另一个问题是我们有{em>两个调用inorder
。如果我们将inorder l
放在尾巴位置,inorder r
不可能在尾巴位置-反之亦然。
解决此问题的一种好方法是连续传递样式。我们将函数放在上面,并将其转换为带有附加参数的辅助函数,以供我们继续使用return
(* convert to helper function, add an extra parameter *)
let rec loop t return =
match t with
| Empty -> ...
| Node (v, l, r) -> ...
延续表示“下一步做什么” ,因此,与其直接从我们的函数中发送值,不如将它们交给延续。这意味着在Empty
情况下,我们将return []
-而不是简单的[]
let rec loop t return =
match t with
| Empty -> return []
| Node (v, l, r) -> ...
对于Node (v, l, r)
,现在有了一个额外的参数,我们可以编写自己的延续,通知loop
接下来要做什么。因此,要构建我们的排序列表,我们需要先loop l
,然后loop r
(反之亦然),然后我们可以append
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))
。我们将像这样编写程序。
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))))
在接下来的步骤中,我们将为延续部分填写实际的lambda语法。
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
。
loop r (fun right -> ...)
您会看到Node
在loop l (fun left -> ...)
分支的尾部位置。 List.append ...
在第一个延续的尾部位置。并且List.append
在第二个延续的尾部位置。如果inorder
是尾递归过程,则List.append
将不会增加堆栈。
请注意,使用Node
对于大树来说可能是一个昂贵的选择。我们的函数每.Last.value
调用一次。您能想到一种避免的方法吗?本练习留给读者。