简体   繁体   English

在 f# 中折叠/递归多路树

[英]Fold / Recursion over Multiway Tree in f#

I am trying to adapt Brian's Fold for Binary Trees ( http://lorgonblog.wordpress.com/2008/04/06/catamorphisms-part-two/ ) to apply for Multiway trees.我正在尝试调整 Brian's Fold for Binary Trees ( http://lorgonblog.wordpress.com/2008/04/06/catamorphisms-part-two/ ) 以申请多路树。

Summarizing from Brian's Blog:来自 Brian 博客的总结:

Data structure:数据结构:

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)))

Binary Tree Fold function二叉树折叠函数

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) 

examples例子

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

Multiway Tree version [not (fully) working] :多路树版本[不(完全)工作]

Data Structure数据结构

type MultiTree = | MNode of int * list<MultiTree>

let Mtree7 = MNode(4, [MNode(2, [MNode(1,[]); MNode(3, [])]);  
                    MNode(6, [MNode(5, []); MNode(7, [])])])

Fold function折叠功能

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) 

Example 1 Returns 28 - seems to work示例 1返回 28 - 似乎有效

let MSumNodes = MFoldTree (fun x acc -> x + acc) 0 Mtree7

Example 2示例 2

Doesn't Run不运行

let MTree6to0 = MFoldTree (fun x acc -> MNode((if x=6 then 0 else x), [acc])) Mtree7

Initially I thought the MFoldTree needed a map.something somewhere but I got it to work with the @ operator instead.最初我认为MFoldTree需要一个map.something某处,但我让它与@运算符一起工作。

Any help on the second example and or correcting what I've done in the MFoldTree function would be great!对第二个示例的任何帮助和/或纠正我在MFoldTree函数中所做的MFoldTree都会很棒!

Cheers干杯

dusiod杜西奥德

The trick is that you need to pass one additional function to fold.诀窍是你需要传递一个额外的函数来折叠。

In Brian's version, the fold function just takes nodeF that is called with the value in the node and the two values produced from the left and right sub-trees.在 Brian 的版本中, fold 函数只需要使用节点中的值和从左右子树产生的两个值调用的nodeF

That is not sufficient for multiway trees.这对于多路树来说是不够的。 Here, we need a function nodeF that is called with the value in the node and the result produced by aggregating all values of the sub-trees.在这里,我们需要一个函数nodeF ,该函数使用节点中的值和通过聚合子树的所有值产生的结果来调用。 But you also need a function - say combineF that combines value produced from multiple sub-trees of a node.但是您还需要一个函数 - 比如combineF ,它结合了从节点的多个子树产生的值。

Your fold function is a good start - you just need to add one more recursive call to process tail :您的 fold 函数是一个好的开始——您只需要再添加一个递归调用来处理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) 

Summing is easy, because we just use the + operator for both functions:求和很容易,因为我们只对两个函数使用+运算符:

let MSumNodes = MFoldTree (+) (+) 0 Mtree7

Filtering the tree is more tricky.过滤树更棘手。 The nodeF function will get the element in the node and a list of child nodes (that is the result of aggregation) and produces a single node. nodeF函数将获取节点中的元素和子节点列表(即聚合的结果)并生成单个节点。 The combineF function will get the result from a first node (that is a MultiTree value) and a list of children produced from the remaining nodes. combineF函数将从第一个节点(即MultiTree值)和从其余节点生成的子节点列表中获取结果。 The initial value produced from an empty tree is an empty list:从空树产生的初始值是一个空列表:

let MTree6to0 = 
  MFoldTree (fun x children -> MNode((if x=6 then 0 else x), children)) 
            (fun head tail -> head::tail) [] Mtree7

Another solution could be另一种解决方案可能是

let rec mfold f a (MNode(x,s)) = f (List.fold (fun a t -> mfold f a t) a s) x

really, we can treat tree as a lineal struct (to fold it).实际上,我们可以将树视为线性结构(折叠它)。

Use case用例

> mfold (+) 0 Mtree7;;
val it : int = 28

Filter is the same with normal fold (because mfold is a normal fold): filter 和 normal fold 一样(因为mfold是普通折叠):

> mfold (fun a x -> if x = 6 then a else x + a) 0 Mtree7;;
val it : int = 22

That function could be generic (as List.fold , Array.fold , ... could be generics).该函数可以是泛型的(如List.foldArray.fold 、... 可以是泛型)。

"but the intention of the second is to return the whole tree modified so that any nodes which had the value 6 for example now have value 0" “但第二个的目的是返回修改后的整个树,以便任何具有例如值为 6 的节点现在具有值为 0”

But that is not a fold computation, is a map !但这不是fold计算,而是map

You can do easilly (treating, again, as a lineal struct)您可以轻松完成(再次将其视为线性结构)

let rec mmap f (MNode(x,s)) = MNode(f x, List.map (mmap f) s)

Use case用例

> 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,[])])])

Again, I suggest to do it for each possible list container ( Seq , List , Array , ...), it enable to user select best strategy in context.同样,我建议为每个可能的列表容器( SeqListArray等)执行此操作,它使用户能够在上下文中选择最佳策略。

Notes:笔记:

  • I'm new in F#, excuse me if some is wrong.我是 F# 新手,如果有些错误,请见谅。
  • stack size should not be a problem, stack level is equal to tree's deep.堆栈大小应该不是问题,堆栈级别等于树的深度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM