通过简单的图形示例在F#中设计类型的一些困难

时间:2016-02-04 08:11:42

标签: f# c#-to-f#

有方向图:

enter image description here

我们正在为它添加节点和边缘:

enter image description here

然后删除其他一些(通过算法,这里没关系):

enter image description here

我曾尝试在F#中做到这一点,但由于我的经验不足,我无法选择正确的架构决策。

open System.Collections.Generic

type Node = Node of int
type OGraph(nodes : Set<Node>,
            edges : Dictionary<Node * int, Node>) =
    member this.Nodes = nodes
    member this.Edges = edges

let nodes = set [Node 1; Node 2; Node 3]
let edges = Dictionary<Node * int, Node>()
Array.iter edges.Add [|
    (Node 1, 10), Node 2;
    (Node 2, 20), Node 3;
|]
let myGraph = OGraph(nodes, edges)

myGraph.Nodes.Add (Node 4)
myGraph.Edges.Add ((Node 2, 50), Node 4)

myGraph.Edges.Remove (Node 2, 20)
myGraph.Nodes.Remove (Node 3)
  1. 如何添加空节点?我的意思是,它可能是3或4甚至100500.如果我们添加没有数字的节点,那么我们如何使用它来创建边缘? myGraph.Edges.Add ((Node 2, 50), ???)在命令式范例中,由于使用了命名引用和Null,它很简单,我们可以创建Node newNode = new Node()然后使用此引用newNode,但似乎在F#中这是一个不好的做法

  2. 我应该指定单独的类型Node和Edge还是使用简单类型?或者可能是其他代表,更复杂?

  3. 最好使用常见的.NET可变集合(HashSet,Dictionary等),或特殊的F#集合(Set,Map等)?如果集合很大,那么每次应该更改整个集合时,在性能方面是可以接受的吗?

2 个答案:

答案 0 :(得分:3)

图表本身很容易建模。您可以这样定义:

type Graph = { Node : int option; Children : (int * Graph) list }

如果愿意,你可以使用类型别名或自定义类型而不是原始int值来修饰它,但这是基本的想法。

您可以对OP中描绘的三个图形进行建模,如下所示。我使用的格式看起来很冗长,但我故意用这种方式格式化值,以使结构更清晰;如果您愿意,可以用更紧凑的形式写出值。

let x1 =
    {
        Node = Some 1;
        Children =
            [
                (
                    10,
                    {
                        Node = Some 2;
                        Children =
                            [
                                (
                                    20,
                                    {
                                        Node = Some 3;
                                        Children = []
                                    }
                                )
                            ]
                    }
                )
            ]
        }
let x2 =
    {
        Node = Some 1;
        Children =
            [
                (
                    10,
                    {
                        Node = Some 2;
                        Children =
                            [
                                (
                                    20,
                                    {
                                        Node = Some 3;
                                        Children = []
                                    }
                                );
                                (
                                    50,
                                    {
                                        Node = None;
                                        Children = []
                                    }
                                )
                            ]
                    }
                )
            ]
        }
let x3 =
    {
        Node = Some 1;
        Children =
            [
                (
                    10,
                    {
                        Node = Some 2;
                        Children =
                            [
                                (
                                    50,
                                    {
                                        Node = Some 3;
                                        Children = []
                                    }
                                )
                            ]
                    }
                )
            ]
        }

请注意使用int option来捕获节点是否有值。

Graph类型是F#记录类型,并为子项使用F#workhorse list。这将是我的默认选择,只有在性能成为问题时才会考虑其他数据类型。列表很容易使用。

答案 1 :(得分:0)

如果这些很容易就是正确的:

  1. 使用选项 - 然后空节点为无
  2. 也许 - 取决于问题
  3. 这取决于您正在解决的具体问题 - F#集合往往是不可变的,有些操作很快,但.NET集合还有其他快速操作。