这实际上是一个双重问题。第一:作为一个来自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中输入一个与另一个?
我在文档中找不到任何关于此(或在线)的内容。感谢。
答案 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}};
如果Verts
和Edges
分配了值,那么就会出现“奇怪的副作用”。但是,有几种方法可以缓解这个问题。
首先,在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} *)
使用此方案,字段键Verts
和Edges
可以是包的私有,并受到保护,完全避免了意外的值分配破坏事物的可能性。
在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,然后可以修改为:这是一个好习惯吗?