这实际上是一个双重问题。首先:作为一个来自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]];这是可取的,因为我可能并不总是想要具有颜色信息的图形数据结构,因此不想在列表中保留一个点(如graph[[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并不相同。那么g[2][1]到底是什么呢?
它看起来几乎是一个真正的标识符,如果是这样的话,我想用它来解决上面问题1的问题。有没有人知道其中的区别,或者如何在Mathematica中输入一个或另一个?
我在文档(或在线)中找不到任何与此相关的内容。谢谢。
发布于 2011-07-29 11:12:54
GraphEdit Rules
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} *)为什么会有这种变化?第一个原因是head graphRecord现在肯定地标识了数据类型,而在此之前它只是一个列表。其次,我们可以定义更多的函数(准方法),这些函数只作用于graphRecord,而不作用于其他函数。例如:
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::
时出现无效参数graphEdgeCount: hi的参数无效
$Aborted
作为所有这一切的最终阐述,可以定义一个宏函数,该函数在给定类型和字段名称的情况下自动定义所有的记录函数。但是,由于此响应已经是TL;DR,因此最好将其留作某天另一个问题的主题。
注意:如果这些函数都是在包的上下文中定义的,则它们的名称将使用首字母大写(例如,MakeGraph而不是makeGraph)。但是要注意,Mathematica已经有很多内置的符号,其中包括单词Graph。
发布于 2011-07-28 02:26:16
我想知道您使用的是哪个版本的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导入的工作方式就是一个很好的例子。
发布于 2011-07-28 01:36:25
我使用InputFormhadn't known about that function before today.找到了问题2的答案
InputForm[g[[2]][[1]]];返回
"Graph"因此,避免问题1中的问题的方法和他们如何定义GraphEdit的答案是在规则中使用字符串作为“标识符”。
问题1,然后可以修改为:这是一个好的做法吗?
https://stackoverflow.com/questions/6848385
复制相似问题