与f#战斗 - 战斗在树的领域 - 特别是计算节点的数量。这是真正令人感兴趣的,因为我想最终在F#中编写的程序涉及多路树,不幸的是它已经开始有点麻烦了 - 我希望你能帮忙!
99 f#系列中的问题61,要求计算二叉树的叶子。解决方案(下面给出)计算节点,但我的问题是不理解
双递归如何循环左(有趣的lacc - >循环右..)
cont (branchF x lacc racc)
是什么,我的印象是cont是“abc”函数,但这只需要两个参数......
loop t id
id的类型为单位 - 我不知道这是如何暗示的
基本上不理解这一点,或者它在树中流动的顺序(调试和步骤通过不证明有用)如果有更简单的例子,预读推荐等,那么请指导我们。
非常感谢您的帮助,有问题的解决方案代码如下:
干杯
TD
type 'a Tree = Empty | Branch of 'a * 'a Tree * 'a Tree
let foldTree branchF emptyV t =
let rec loop t cont =
match t with
| Empty ->
cont emptyV
| Branch (x, left, right) ->
loop left (fun lacc ->
loop right (fun racc ->
cont (branchF x lacc racc)))
loop t id
let counLeaves tree =
foldTree (fun abc lc rc ->
if lc + rc = 0 then 1
else 1 + lc + rc) 0 tree
let Tree1 = Branch ('x', Branch ('x', Empty, Empty),Branch ('x', Empty, Branch ('x', Empty, Branch ('x', Empty, Empty))))
let result = counLeaves Tree1
答案 0 :(得分:6)
顾名思义,foldTree
定义了自定义Tree
类型的折叠函数。
定义foldTree
的简单方法可能是:
let rec foldTreeNaive accFun init = function
| Empty -> init
| Branch (x, left, right) ->
let lacc = foldTreeNaive accFun init left
let racc = foldTreeNaive accFun init right
accFun x lacc racc
这个函数的问题在于,如果被折叠的树很深,它可能会进行非常深的递归调用,因为在调用累加器函数之前必须为节点完成递归调用。例如,以下内容导致堆栈溢出异常:
let unbalanced = [1..100000] |> List.fold (fun t i -> Branch(i, t, Empty)) Empty
let nodeCount = foldTreeNaive (fun _ lc rc -> lc + rc + 1) 0 unbalanced
避免这种堆栈溢出的常用方法是使函数tail递归,但是在这种情况下这似乎是不可能的,因为有两个递归调用,而不是折叠列表时所需的调用。
foldTree
是使用本地loop
函数定义的。这个函数很有意思,因为它是使用continuation passing style定义的。在CPS中,每个功能都需要额外的继续'传递计算结果的函数,负责决定接下来发生的事情。请注意loop
是尾递归的,因此避免了foldTreeNaive
的溢出问题。
loop
函数的类型是:
Tree<'a> -> ('b -> 'c) -> 'c
其中&#39; a是树中节点的类型,&b; b是累加器类型,&c; c是连续函数的结果。
对于叶节点,继续传递传递给foldTree
函数的空累加器值。
在Branch
情况下折叠非空树时,折叠的结果取决于左右子树的结果。这是递归完成的,首先是折叠左子树,然后是右边。对于左子树的递归调用,loop
必须构建一个新的延续来接收结果,这是
(fun lacc ->
loop right (fun racc ->
cont (branchF x lacc racc))
功能。这个延续的作用是对正确的子树进行递归调用,传递另一个延续以接收该折叠的结果。调用该延续时,左侧和右侧子树的结果可在lacc
和racc
中获得。此时,可以使用当前节点的值和左右子树的结果来调用节点的累积函数。然后将此函数的结果传递给传递给loop
的原始延续。
然后行中的loop
函数调用foldTree
函数:
loop t id
这里,id
是继续,它将接收树的根节点的折叠结果。由于这是所需的值,id
只返回其参数而不进行修改。