使用F#实现树构建器

时间:2013-06-28 12:33:59

标签: algorithm f#

我对F#很新,我想实现以下问题的解决方案: 从随机顺序发现的一系列磁盘路径(例如“C:\ Hello \ foo”“C:”,“C:\ Hello \ bar”等......)如何构建(有效)树。 假设:序列有效,这意味着可以有效地创建树。

所以我尝试使用递归函数(下面的“mergeInto”)实现,该函数将树“就地”与字符串列表(分割路径称为“branch”)合并在一起

这是我的实现,不变性可以防止对输入树的副作用,所以我尝试使用ref单元格作为输入树,但是我遇到了递归的困难。任何解决方案?

open Microsoft.VisualStudio.TestTools.UnitTesting

type Tree =
    |Node of string*list<Tree>
    |Empty

let rec branchToTree (inputList:list<string>) =
    match inputList with
        | [] -> Tree.Empty
        | head::tail ->  Tree.Node (head, [branchToTree tail])

//branch cannot be empty list
let rec mergeInto (tree:Tree ref) (branch:list<string>) =
    match !tree,branch with
        | Node (value,_), head::tail when String.op_Inequality(value, head) -> raise (ApplicationException("Oops invariant loop broken"))
        | Node (value,_), [_] -> ignore() //the branch is singleton and by loop invariant its head is the current Tree node -> nothing to do.
        | Node (value,children), _ -> 
                                let nextBranchValue = branch.Tail.Head //valid because of previous match

                                //broken attempt to retrieve a ref to the proper child
                                let targetChild = children 
                                                |> List.map (fun(child) -> ref child)
                                                |> List.tryFind (fun(child) -> match !child with
                                                                                        |Empty -> false
                                                                                        |Node (value,_) -> value = nextBranchValue)
                                match targetChild with
                                    |Some x -> mergeInto x branch.Tail //a valid child match then go deeper. NB: branch.Tail cannot be empty here
                                    |None -> tree := Node(value, (Node (nextBranchValue,[])) :: children)//attach the next branch value to the children
        | Empty,_ -> tree := branchToTree branch

[<TestClass>]
type TreeTests () = 
    [<TestMethod>]
    member this.BuildTree () =
        let initialTree = ref Tree.Empty
        let branch1 = ["a";"b";"c"]
        let branch2 = ["a";"b";"d"]

        do mergeInto initialTree branch1
        //-> my tree is ok
        do mergeInto initialTree branch2
        //->not ok, expected a
        //                   |
        //                   b
        //                  / \
        //                 d   c 

1 个答案:

答案 0 :(得分:2)

您无法reflist中的元素进行ref,更改list,然后期望Tree中的项目发生变化。如果您真的想这样做,那么您应该将引用放入type Tree = |Node of string*list<Tree ref> |Empty let rec branchToTree (inputList:list<string>) = match inputList with | [] -> Tree.Empty | head::tail -> Tree.Node(head, [ref (branchToTree tail)]) 类型。

List.map (fun(child) -> ref child)

如果您这样做,请删除{{1}}部分,然后您的代码就可以了。

您可能对zippers感兴趣,它允许您做类似但没有变异的事情。