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