dus*_*iod 8 tree f# traversal fold multiway-tree
我正在努力使Brian's Fold for Bianary Trees(http://lorgonblog.wordpress.com/2008/04/06/catamorphisms-part-two/)适用于Multiway树.
摘自Brian的博客:
数据结构:
type Tree<'a> =
| Node of (*data*)'a * (*left*)Tree<'a> * (*right*)Tree<'a>
| Leaf
let tree7 = Node(4, Node(2, Node(1, Leaf, Leaf), Node(3, Leaf, Leaf)),
Node(6, Node(5, Leaf, Leaf), Node(7, Leaf, Leaf)))
Run Code Online (Sandbox Code Playgroud)
二叉树折叠功能
let FoldTree nodeF leafV tree =
let rec Loop t cont =
match t with
| Node(x,left,right) -> Loop left (fun lacc ->
Loop right (fun racc ->
cont (nodeF x lacc racc)))
| Leaf -> cont leafV
Loop tree (fun x -> x)
Run Code Online (Sandbox Code Playgroud)
例子
let SumNodes = FoldTree (fun x l r -> x + l + r) 0 tree7
let Tree6to0 = FoldTree (fun x l r -> Node((if x=6 then 0 else x), l, r)) Leaf tree7
Run Code Online (Sandbox Code Playgroud)
多路树版 [不(完全)工作]:
数据结构
type MultiTree = | MNode of int * list<MultiTree>
let Mtree7 = MNode(4, [MNode(2, [MNode(1,[]); MNode(3, [])]);
MNode(6, [MNode(5, []); MNode(7, [])])])
Run Code Online (Sandbox Code Playgroud)
折叠功能
let MFoldTree nodeF leafV tree =
let rec Loop tree cont =
match tree with
| MNode(x,sub)::tail -> Loop (sub@tail) (fun acc -> cont(nodeF x acc))
| [] -> cont leafV
Loop [tree] (fun x -> x)
Run Code Online (Sandbox Code Playgroud)
示例1 返回28 - 似乎工作
let MSumNodes = MFoldTree (fun x acc -> x + acc) 0 Mtree7
Run Code Online (Sandbox Code Playgroud)
例2
不跑
let MTree6to0 = MFoldTree (fun x acc -> MNode((if x=6 then 0 else x), [acc])) Mtree7
Run Code Online (Sandbox Code Playgroud)
最初我认为MFoldTree需要一个map.something地方,但我让它与@运营商合作.
对第二个例子的任何帮助和/或纠正我在MFoldTree函数中所做的事情都会很棒!
干杯
dusiod
诀窍是你需要传递一个额外的功能来折叠.
在Brian的版本中,fold函数只需要nodeF使用节点中的值以及从左侧和右侧子树生成的两个值进行调用.
这对于多路树来说是不够的.在这里,我们需要一个nodeF用节点中的值调用的函数,以及通过聚合子树的所有值产生的结果.但是你还需要一个函数 - 比如说combineF它结合了节点的多个子树产生的值.
你的折叠函数是一个好的开始 - 你只需要再添加一个递归调用来处理tail:
let MFoldTree nodeF combineF leafV tree =
let rec Loop trees cont =
match trees with
| MNode(x,sub)::tail ->
// First, process the sub-trees of the current node and get
// a single value called 'accSub' representing (aggregated)
// folding of the sub-trees.
Loop sub (fun accSub ->
// Now we can call 'nodeF' on the current value & folded sub-tree
let resNode = nodeF x accSub
// But now we also need to fold all remaining trees that were
// passed to us in the parameter 'trees'..
Loop tail (fun accTail ->
// This produces a value 'accTail' and now we need to combine the
// result from the tail with the one for the first node
// (which is where we need 'combineF')
cont(combineF resNode accTail) ))
| [] -> cont leafV
Loop [tree] (fun x -> x)
Run Code Online (Sandbox Code Playgroud)
求和很简单,因为我们只使用+运算符来实现这两个功能:
let MSumNodes = MFoldTree (+) (+) 0 Mtree7
Run Code Online (Sandbox Code Playgroud)
过滤树更加棘手.该nodeF函数将获取节点中的元素和子节点列表(即聚合的结果)并生成单个节点.该combineF函数将从第一个节点(即一个MultiTree值)获得结果,并从其余节点生成子项列表.从空树生成的初始值是一个空列表:
let MTree6to0 =
MFoldTree (fun x children -> MNode((if x=6 then 0 else x), children))
(fun head tail -> head::tail) [] Mtree7
Run Code Online (Sandbox Code Playgroud)
另一个解决方案可能是
let rec mfold f a (MNode(x,s)) = f (List.fold (fun a t -> mfold f a t) a s) x
Run Code Online (Sandbox Code Playgroud)
实际上,我们可以将树视为一个直线结构(折叠它).
用例
> mfold (+) 0 Mtree7;;
val it : int = 28
Run Code Online (Sandbox Code Playgroud)
过滤器与正常折叠相同(因为mfold是正常折叠):
> mfold (fun a x -> if x = 6 then a else x + a) 0 Mtree7;;
val it : int = 22
Run Code Online (Sandbox Code Playgroud)
该函数可以是通用的(如List.fold,Array.fold...可能是仿制药).
"但第二个意图是返回修改的整个树,以便任何具有值6的节点现在具有值0"
但这不是一个fold计算,是一个map!
你可以做easyilly(再次作为一个线性结构处理)
let rec mmap f (MNode(x,s)) = MNode(f x, List.map (mmap f) s)
Run Code Online (Sandbox Code Playgroud)
用例
> mmap (fun x -> if x=6 then 0 else x) Mtree7;;
val it : MultiTree =
MNode
(4,
[MNode (2,[MNode (1,[]); MNode (3,[])]);
MNode (0,[MNode (5,[]); MNode (7,[])])])
Run Code Online (Sandbox Code Playgroud)
同样,我认为这样做对每个可能的列表容器(Seq,List,Array,...),使其能够以用户上下文菜单中选择最好的策略.
笔记: