Graph OCaml有向图顶点模

Graph OCaml有向图顶点模,graph,module,ocaml,vertex,directed-graph,Graph,Module,Ocaml,Vertex,Directed Graph,我看过一些顶点签名图,甚至提出了我自己的: module type VERTEX = sig type t type label val equal : t -> t -> bool val create : label -> t val label : t -> label end 但我完全不知道如何将其作为一个模块来实现。t和标签应该是什么类型?如何基于标签创建t?如何从t中获得标签?基于签名实现模块就像一个小拼图。以下

我看过一些顶点签名图,甚至提出了我自己的:

module type VERTEX = sig
    type t
    type label

    val equal : t -> t -> bool
    val create : label -> t
    val label : t -> label  
end

但我完全不知道如何将其作为一个模块来实现。t和标签应该是什么类型?如何基于标签创建t?如何从t中获得标签?

基于签名实现模块就像一个小拼图。以下是我将如何分析它:

我在阅读该签名时的第一句话是,在该签名中无法构建
label
类型的值。因此,我们的实现需要更大一些,可能需要指定
type label=string

现在,我们有:

val create : label -> t
val label : t -> label
这是一个双射(类型是“等价的”)。实现这一点的最简单方法是定义
typet=label
,这样它实际上只是一种类型,但从模块外部看,您不知道这一点

其余的是

type t
val equal: t -> t -> bool
我们说的是
label=string
,和
t=label
。所以
t=string
,而
equal
是字符串相等

轰!我们到了:

module String_vertex : VERTEX with type label = string = struct
  type label = string
  type t = string

  let equal = String.equal

  let create x = x
  let label x = x
end
如果要在同一个文件中定义,则类型为label=string的
顶点部分仅适用。否则,您可以执行以下操作:

(* string_vertex.ml *)
type label = string
type t = string

let equal = String.equal

let create x = x
let label x = x
任何取
顶点的函子F都可以用
F(String\u-VERTEX)
调用。 最好的做法是创建
string\u vertex.mli
,内容为
包含类型为label=string的vertex

我是的作者,所以这个问题直接触动了我的心,我无法回避。老实说,这个问题我在离线时被问了数百万次,但始终无法给出一个好的答案

真正的问题是,库中的图形界面都乱七八糟。我们启动Graphlib是为了修复它们。然而,OCamlGraph是一个有价值的图形算法库,因此我们限制自己与OCamlGraph接口兼容。我们的主要问题是,现在仍然是这个顶点接口,它基本上在标签集和节点集之间建立了一个双射。人们通常会发现这一点,因为这没有意义-如果标签和顶点是相同的,为什么我们需要两种不同的类型,一种用于标签,另一种用于顶点

实际上,
VERTEX
接口的最简单实现是以下模块

module Int : VERTEX with type label = int = struct
  type t = int
  type label = int
  let create x = x 
  let label x = x
end
在这种情况下,我们确实在标签集和顶点集之间有一个平凡的双射(通过恒等式内函子)

然而,更深入的观察,我们发现一个签名

val create : label -> t
val label : t -> label
实际上不是双射,因为双射是一对一的映射。它不是类型系统真正需要或强制执行的。例如,
create
函数可以是
label
t
的满射,其中
label
是一系列顶点中的一些独特元素。相应地,
label
函数可能是一个遗忘函子,它返回独特的标签并遗忘其他所有内容

基于这种方法,我们可以有另一种实现:

  module Labeled = struct
    type label = int
    type t = {
      label : label;
      data : "";  
    }

    let create label = {label; data = ""}
    let label n = n.label
    let data n = n.data
    let with_data n data = {n with data}
    let compare x y = compare x.label y.label
  end
在该实现中,我们使用标签作为节点的标识,并且可以将任意属性附加到节点。在此解释中,
create
函数将所有节点集划分为一组,其中类的所有成员共享相同的标识,即它们在不同的时间或空间点表示相同的真实世界实体。比如说,

type color = Red | Yellow | Green
module TrafficLight = struct 
   type label = int
   type t = {
     id : label;
     color : color
   }

   let create id = {id; color=Red}
   let label t = t.id
   let compare x y = compare x.id y.id
   let switch t color = {t with color}
   let color t = t.color
end
在这个模型中,我们用它的id号来表示交通灯。“颜色”属性不影响交通灯的标识(如果交通灯切换到另一种颜色,它仍然是同一个交通灯,尽管在函数式编程语言中,它由两个不同的对象表示)

上述表示法的主要问题是,在所有的图形教科书中,标签都是以相反的含义使用的——作为不透明属性。在教科书中,他们将交通灯的颜色作为标签。节点本身将被表示为一个int。这就是为什么我说OCamlGraph接口是混乱的(因此Graphlib接口也是混乱的)。因此,如果您不想与教科书相矛盾,那么应该使用未标记的图(with
int
可能是节点的最佳表示形式)。如果需要将属性附加到节点,可以使用外部有限映射,即数组、映射、关联列表或任何其他字典。否则,您需要记住标签不是标签,而是节点

说到这里,让我们为图顶点指定一个更好的接口:

module type VERTEX = sig 
  type id
  type label
  type t

  val create : id -> t
  val id : t -> id
  val label : t -> label
  val with_label : t -> label -> label
end
建议的接口与您的接口兼容(因此与OCamlGraph兼容),因为它是同构模重命名(即,我们将
标签
重命名为
id
)。它还允许我们创建有效的未标记节点,其中
id=t
,以及在不依赖外部映射的情况下将任意信息附加到节点