Mathematica Rules和GraphEdit返回的对象有什么区别?

时间:2011-07-27 17:08:35

标签: wolfram-mathematica mathematical-expressions

这实际上是一个双重问题。第一:作为一个来自OO编程背景的人,我发现Mathematica使用列表作为一切有点烦人的基础。所以这就是mathematica程序员(据我所知)可能如何定义图形:

graph={{1, 2, 3, 4, 5}, {1->2, 2->4, 4->4, 4->5}};

然后程序员只需要记住那个

graph[[1]] 

指的是顶点列表和

graph[[2]]

指的是边缘列表(在本例中定义为一组规则。)

所以,我正在学习Mathematica中的规则,我看到了一个让我的数据结构更加面向对象的机会。我选择定义一个图形:

graph={Verts->{1,2,3,4,5}, Edges->{1->2, 2->4, 4->4, 4->5}};

然后通过

引用顶点和边(分别)
Verts/.graph
Edges/.graph

然而,如果某个其他Mathematica文件已将Verts或Edges定义为某个地方的全局变量,则可能会产生奇怪的副作用,因为规则的左侧不是标识符,但本身就是一个对象。

问题1是这样的:这是一个很好的实践,还是创建Mathematica数据结构的坏实践?我这样做的原因之一是我可以附加任意属性,比如颜色:

AppendTo[graph, Colors->{Red, Red, Blue, Red, Red}]; (* Labels ea. vert with a color *)

我的函数不必知道添加特定属性的确切顺序。例如,您可能将函数GetColor定义为:

GetColor[graph_, vertIdx_]:=(Colors/.graph)[[vertIdx]];

这是首选,因为我可能并不总是希望图形数据结构具有颜色信息,因此不希望在列表中保留一个点(如图[[[3]]])以获取颜色信息

第二:我看到GraphEdit返回的东西看起来像我上面描述的规则。例如,如果我执行(并绘制图形)

Needs["GraphUtilities`"];
g = GraphEdit[];
g[[2]]

我输出如下:

Graph->{1->2,3->3,4->4,5->4}

看起来像一个规则!所以我试试这个:

Graph/.g[[2]]

期待

{1->2,3->3,4->4,5->4}

返回。但相反,输出只是

Graph

但如果我改为执行

g[[2]][[1]] /. g[[2]]

我得到了预期的输出,

{1->2,3->3,4->4,5->4}

这意味着g [[2]]确实是一个规则,但由于某种原因g [[2]] [[1]](如果执行打印Graph)与输入Graph不同。那么g [[2]] [[1]]到底是什么?

看起来它几乎就像是一个真正的标识符,如果是这样的话,我想用它来解决上面问题1的问题。任何人都知道差异,或者如何在Mathematica中输入一个与另一个?

我在文档中找不到任何关于此(或在线)的内容。感谢。

3 个答案:

答案 0 :(得分:4)

我想知道您使用的是什么版本的Mathematica?

图形理论更紧密地集成到V8的核心中,这在很大程度上是一种改进。您可以以面向对象程序员可能喜欢的方式与图形交互。在V8中,我会像这样执行您的示例:

g = Graph[Range[5], {1 -> 2, 2 -> 4, 4 -> 4, 4 -> 5}];
g = SetProperty[{g, 3}, VertexStyle -> Red]

然后我可以像这样查询属性:

PropertyValue[{g, 3}, VertexStyle]

不幸的是,大多数较旧的图论功能在V8中并不能很好地发挥作用。虽然,如果您对上下文规范要小心,可以使用它。在V7中,我可以像这样访问GraphEdit的输出:

Needs["GraphUtilities`"];
g = GraphEdit[];

然后,

{vertices, edges} = {"VertexLabels", "Graph"} /. Rest[g]

获得值得传递给其他函数的内容,例如GraphPlot

这部分回答了你的问题,这是否是一个合理的代表。这种类型的表示使得通过替换规则访问信息变得容易。 XML导入的工作方式就是一个很好的例子。

答案 1 :(得分:4)

GraphEdit规则

GraphEdit返回一个列表,其第一个元素是Graphics对象,其余元素是描述图形的规则。每个规则的左侧是一个字符串,而不是一个符号。您可以使用g // FullForm确定此问题。要提取图形规则,必须忽略列表的第一个元素,例如

"Graph" /. Drop[g, 1]

模拟记录类型

如您所述,实现类似记录的数据类型是一种合理的方法:

graph={Verts->{1,2,3,4,5}, Edges->{1->2, 2->4, 4->4, 4->5}};

如果VertsEdges分配了值,那么就会出现“奇怪的副作用”。但是,有几种方法可以缓解这个问题。

首先,在Mathematica中有一个非常普遍的约定,即避免将值(特别是OwnValues)赋值给具有大写首字母的符号。 Wolfram将所有顶级变量添加到$的前缀,例如$Context。如果你坚持这些惯例,你会得到一些安全措施。

其次,使用Packages提供单独的命名空间。在您定义的包的范围内,您可以完全控制用作字段名称的符号的绑定。

第三,您可以使用Protect来防止字段名称分配给它们。

实现这些记录类型时,可以遵循LISP习惯用法并定义构造函数和访问器函数。对于图形示例,这些函数可能如下所示:

ClearAll[makeGraph, graphVertices, graphEdges]
makeGraph[vertices_, edges_] := {Verts -> vertices, Edges -> edges}
graphVertices[graph_] := Verts /. graph
graphEdges[graph_] := Edges /. graph

因此将使用这些功能:

graph = makeGraph[{1,2,3,4,5}, {1->2,2->4,4->4,4->5}]
(* {Verts -> {1, 2, 3, 4, 5}, Edges -> {1 -> 2, 2 -> 4, 4 -> 4, 4 -> 5}} *)

graphVertices[graph]
(* {1, 2, 3, 4, 5} *)

graphEdges[graph]
(* {1 -> 2, 2 -> 4, 4 -> 4, 4 -> 5} *)

使用此方案,字段键VertsEdges可以是包的私有,并受到保护,完全避免了意外的值分配破坏事物的可能性。

在Mathematica中,使用表达式的Head来识别其类型是非常常见的。我们可以遵循这个习惯用法并重新定义我们的记录功能:

ClearAll[makeGraph, graphVertices, graphEdges]
makeGraph[vertices_, edges_] := graphRecord[Verts -> vertices, Edges -> edges]
graphVertices[graphRecord[rules___]] := Verts /. {rules}
graphEdges[graphRecord[rules___]] := Edges /. {rules}

这些与前面定义之间唯一的重大差异是图形对象现在由graphRecord[...]形式的表达式代替{...}表示:

graph = makeGraph[{1,2,3,4,5}, {1->2,2->4,4->4,4->5}]
(* graphRecord[Verts -> {1, 2, 3, 4, 5}, Edges -> {1->2, 2->4, 4->4, 4->5}] *)

graphVertices[graph]
(* {1, 2, 3, 4, 5} *)

graphEdges[graph]
(* {1 -> 2, 2 -> 4, 4 -> 4, 4 -> 5} *)

为什么要改变?第一个原因是头graphRecord现在肯定地识别数据的类型,而在它只是一个列表之前。其次,我们可以定义仅对graphRecord s起作用的其他函数(准方法)。例如:

graphEdgeCount[r_graphRecord] := graphEdges[r] // Length
graphEdgeCount[x_] := (Message[graphEdgeCount::invArg, x]; Abort[])
graphEdgeCount::invArg = "Invalid argument to graphEdgeCount: ``";

使用:

graphEdgeCount[graph]
(* 4 *)

graphEdgeCount["hi"]
在评估graphEdgeCount :: invArg期间:graphEdgeCount的参数无效:hi
$中止

作为对所有这些的最后阐述,可以定义一个宏函数,该函数在给定类型和字段名称的情况下自动定义所有记录函数。但是,由于这个响应已经是TL; DR,这可能最好留作另一个问题的主题。

注意:如果这些功能都是在包的上下文中定义的,那么它们的名称将使用首字母大写(例如MakeGraph而不是makeGraph)。但请注意,Mathematica已经有许多内置符号,其中包含单词Graph

答案 2 :(得分:0)

我使用InputForm []找到问题2的答案(在今天之前还不知道该函数。)

InputForm[g[[2]][[1]]];

返回

"Graph"

因此,似乎避免问题1中的问题的方法以及他们如何定义GraphEdit内容的答案是将规则中的字符串用作“标识符”。

问题1,然后可以修改为:这是一个好习惯吗?