F#实现图及其部分操作

这是在工作之余弄了些这个,做的也不太好,留个纪念,今后要用可以做个笔记:)

namespace FSharp.Graph
#if INTERACTIVE
#r "System.Xml.dll"
#r "System.Xml.Linq"
#endif
open System
open System.Xml.Linq
open System.Xml
type Graph() = 
    let mutable nodes = []
    let mutable edges = []

    member this.Nodes with get() = nodes
    member this.Edges with get() = edges

    member this.CreateNode(id) = 
        match this.FindNode(id) with
            | Some(n) -> None
            | None -> 
                let node = Node(ID=id)
                nodes <- nodes @ [ node ]
                Some node
    member this.CreateEdgeFromNode(from:Node, ``to``:Node, id) = 
        match this.FindEdge id with
        | Some(edge) -> None
        | _ -> 
            let edge = new Edge(from,``to``,id)
            from.AddOutgoingEdge(edge)
            ``to``.AddIncomingEdge(edge)

            edges <- edges @ [edge]
            Some edge

    member this.CreateEdgeFromID(from, ``to``, id) = 
        let fromNode = this.FindNode(from)
        let toNode = this.FindNode(``to``)
        match fromNode, toNode with
            | Some(n0), Some(n1) -> this.CreateEdgeFromNode(n0, n1, id)
            | _ -> None

    member this.FindNode(id) = 
        (nodes:Node list) |> Seq.tryFind(fun n -> n.ID = id)
    member this.FindEdge(id) = 
        (edges:Edge list) |> Seq.tryFind(fun edge -> edge.ID = id)

    member this.RemoveEdge(edge:Edge) = 
        (edge.FromNode:Node).RemoveOutgoingEdge(edge)
        (edge.ToNode:Node).RemoveIncomingEdge(edge)
        edges <- edges |> List.filter (fun n -> n<>edge)
    member this.RemoveNode(node:Node) = 
        node.OutgoingEdges @ node.IncomingEdges |> List.iter this.RemoveEdge
        nodes <- nodes |> List.filter (fun n -> n<>node)  

    member this.CreateGraphFormDGML(path : string) =
        let doc = new System.Xml.XmlDocument()
        doc.Load(path)
        
        let directedGraphChildNodes=
            doc.ChildNodes.[1].ChildNodes
        let directedGraphChildList = seq{for i in 0..(directedGraphChildNodes.Count - 1) do yield directedGraphChildNodes.ItemOf(i)}
        let getElemOfGraph (nodeOrEdage : String) = 
            query{
                for i in directedGraphChildList do
                where(i.Name = nodeOrEdage)
                select i}
            |> Seq.map(fun x -> 
                x.ChildNodes )                     

            |> Seq.map(fun x -> 
                        let nodes = seq{for i in 0..(x.Count - 1) do yield x.ItemOf(i) }
                        nodes)                        
            |> Seq.concat
            
        let nodes = getElemOfGraph "Nodes"
        let links = getElemOfGraph "Links"
        nodes        
        |> Seq.iter(fun xmlNode -> 
                                this.CreateNode(xmlNode.Attributes.[0].Value) |> ignore )                            
        links
            |> Seq.iter(fun xmlNode ->
                                let from = this.FindNode(xmlNode.Attributes.[0].Value).Value
                                let ``to`` = this.FindNode(xmlNode.Attributes.[1].Value).Value
                                let xmlElement = xmlNode :?> XmlElement
                                let mutable id = ""
                                if(xmlElement.GetAttribute("Label") = "") then
                                    id <- "from" + from.ID + "To" + ``to``.ID
                                else
                                    id <- xmlElement.GetAttribute("Label")
                                           
                                this.CreateEdgeFromNode(from,``to``,id) |> ignore
                                )

and Node() =
    let mutable incomingEdges = []
    let mutable outgoingEdges = [] 

    member val ID = Unchecked.defaultof<_> with get, set
    member val Data = Unchecked.defaultof<_> with get, set    
    member this.IncomingEdges with get() = incomingEdges
    member this.OutgoingEdges with get() = outgoingEdges
    

    member this.AddIncomingEdge(edge:Edge) = 
        if edge.ToNode = this then
            incomingEdges <- incomingEdges |> List.append [edge]
    member this.AddOutgoingEdge(edge:Edge) = 
        if edge.FromNode = this then
            outgoingEdges <- outgoingEdges |> List.append [edge]

    member this.RemoveIncomingEdge(edge:Edge) = 
        incomingEdges <- incomingEdges |> List.filter (fun n -> n<>edge)
    member this.RemoveOutgoingEdge(edge:Edge) = 
        outgoingEdges <- outgoingEdges |> List.filter (fun n -> n<> edge)

    override this.ToString() =
        sprintf "Node(%A)" this.ID

and Edge() =  
    member val ID = Unchecked.defaultof<_> with get, set
    member val FromNode = Unchecked.defaultof<_> with get, set
    member val ToNode = Unchecked.defaultof<_> with get, set


    new(fromNode:Node,toNode:Node,iD) as this = 
        new Edge()        
        then
            this.FromNode <- fromNode
            this.ToNode <- toNode
            this.ID <- iD                                      

这些都是在VS2012上实现的。

利用方法member this.CreateGraphFormDGML(path : string),可以用DGML文件生成图,利用DGML文件我们可以手动的画出自己想要的图,能够自己定义图中各节点之间的关系,也可以利用这个函数检查自己需要实现的一些其他图的算法。

代码中有一点需要指出的是:and 关键字。 and关键字用来连接两个或者多个相互调用的类或者函数。

代码中的“member val ID = Unchecked.defaultof<_> with get, set” 是F#3.0(VS2012)中的新特性—— 属性构造器,新的特性方便了用户定义类的属性。当然在F#3.0之前的版本,我们可以这样实现

    let mutable _id = Unchecked.defaultof<_>
    member this.ID
        with get() =
            _id
        and set(v) =
            _id <- v

关于Unchecked.defaultof<_>,这个也就相当于null

原文地址:https://www.cnblogs.com/FsharpZack/p/2759928.html