Mathematica 规则和 GraphEdit 返回的对象有什么区别?
这实际上是一个双重问题。首先:作为一个有面向对象编程背景的人,我发现 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)与键入 Graph 不同。那么 g[[2]][[1]] 到底是什么?
看起来几乎就像是一个真正的标识符,如果是这样,我想用它来解决上面问题 1 的问题。有人知道其中的区别,或者如何在 Mathematica 中输入其中之一吗?
我在文档(或在线)中找不到任何有关此内容的内容。谢谢。
This is actually a two-fold question. First: as someone coming from an OO programming background, I find Mathematica's use of lists as the basis of everything a bit annoying. So here is how a mathematica programmer (as far as I can tell) might define a graph:
graph={{1, 2, 3, 4, 5}, {1->2, 2->4, 4->4, 4->5}};
and then the programmer would just have to remember that
graph[[1]]
refers to the list of vertices and
graph[[2]]
refers to the list of edges (in this case defined as a set of rules.)
So, I was learning about the rules in Mathematica and I saw an opportunity to make my data structures a little more object-oriented feeling. I chose to define a graph something like:
graph={Verts->{1,2,3,4,5}, Edges->{1->2, 2->4, 4->4, 4->5}};
and then refer to vertices and edges (respectively) by
Verts/.graph
Edges/.graph
This can have weird side effects, however, if some other Mathematica file has defined Verts or Edges as a global variable somewhere, however, since the left hand side of the rule is not an identifier, but is itself an object.
So question 1 is this: is this a good practice, or a bad one for creating Mathematica data structures? One of the reasons I'm doing it this way is so that I can attach arbitrary properties, say colors:
AppendTo[graph, Colors->{Red, Red, Blue, Red, Red}]; (* Labels ea. vert with a color *)
and my functions don't have to know the exact order particular properties were added. For instance you might have a function GetColor defined like:
GetColor[graph_, vertIdx_]:=(Colors/.graph)[[vertIdx]];
and this is preferable, because I might not always want to have graph data structures that have Color info and so don't want to reserve a spot in the list (like graph[[[3]]]) for color information.
Second: I see that GraphEdit returns something that looks like the rules I've described above. For instance, if I execute (and draw a graph)
Needs["GraphUtilities`"];
g = GraphEdit[];
g[[2]]
I get output like:
Graph->{1->2,3->3,4->4,5->4}
which looks like a rule! So I try this:
Graph/.g[[2]]
expecting to have
{1->2,3->3,4->4,5->4}
returned. But instead the output is just
Graph
But if I instead execute
g[[2]][[1]] /. g[[2]]
I get the expected output,
{1->2,3->3,4->4,5->4}
which means that g[[2]] really is a rule, but for some reason g[[2]][[1]] (which if executed prints Graph) is not the same as typing Graph. So what the heck is g[[2]][[1]]?
It seems almost like it is a real identifier, which if so, I would like to use to solve the problems with question 1 above. Anyone know the difference, or how to type one vs. the other into Mathematica?
I cannot find anything in the documentation about any of this (or online). Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
GraphEdit Rules
GraphEdit
返回一个列表,其第一个元素是Graphics
对象,其余元素是描述图形的规则。每个规则的左侧是一个字符串,而不是一个符号。您可以使用g // FullForm
来确定这一点。要提取图形规则,您必须忽略列表的第一个元素,例如模拟记录类型
这是实现您建议的类似记录的数据类型的合理方法:
确实,如果
Verts
和Edges
被赋值,然后就会发生“奇怪的副作用”。然而,有几种方法可以缓解这个问题。首先,Mathematica 中有一个极其广泛的约定,以避免将值(特别是
OwnValues
)分配给首字母大写的符号。 Wolfram 在所有顶级变量前加上$
前缀,例如$Context
。如果您遵守这些约定,您就会获得一定程度的安全性。其次,使用 Packages 提供单独的命名空间。在您定义的包的范围内,您可以完全控制用作字段名称的符号的绑定。
第三,您可以使用 Protect 来防止为字段名称分配值。
在实现这些记录类型时,可以遵循 LISP 习惯用法并定义构造函数和访问函数。对于图形示例,这些函数可能如下所示:
这些函数将这样使用:
使用此方案,字段键
Verts
和Edges
可以是包私有的并受到保护,完全避免了意外的价值分配破坏事物的可能性。在 Mathematica 中,使用表达式的
Head
来标识其类型是非常常见的。我们可以遵循这个习惯用法并重新定义我们的记录函数:这些与前面的定义之间的唯一实质性区别是图形对象现在由
graphRecord[...]
形式的表达式表示而不是{...}
:为什么要改变?第一个原因是头
graphRecord
现在可以肯定地标识数据类型,而之前它只是一个列表。其次,我们可以定义更多的函数(准方法),它们仅作用于 graphRecord ,而不作用于其他任何东西。例如:使用:
作为对所有这一切的最后阐述,可以定义一个宏函数,该函数在给定类型和字段名称的情况下自动定义所有记录函数。然而,由于这个回答已经太长了;DR,这可能最好留作有一天另一个问题的主题。
注意:如果这些函数都是在包的上下文中定义的,则它们的名称将使用首字母大写(例如
MakeGraph
而不是makeGraph
)。但请注意,Mathematica 已经有许多内置符号,其中包括单词Graph
。GraphEdit Rules
GraphEdit
returns a list whose first element is aGraphics
object and whose remaining elements are rules that describe the graph. The left-hand side of each rule is a string, not a symbol. You can determine this usingg // FullForm
. To extract the graph rules, you must ignore the first element of the list, e.g.Simulating Record Types
It is a reasonable approach to implement record-like data types as you propose:
It is true that if
Verts
andEdges
were assigned values, then "weird side effects" would occur. However, there are a couple of ways to mitigate that problem.First, there is an extremely widespread convention within Mathematica to avoid assigning values (specifically
OwnValues
) to symbols with upper case initial letters. Wolfram prefixes all top-level variables with$
, e.g.$Context
. If you stick to these conventions, you get some measure of safety.Second, there is provision for separate namespaces using Packages. Within the bounds of a package you define, you can have complete control over the bindings of symbols that you use as field names.
Third, you can use Protect to prevent field names from having values assigned to them.
When implementing these record types, one could follow a LISP idiom and define constructor and accessor functions. For the graph example, these functions could look something like this:
These functions would be used thus:
Using this scheme, the field keys
Verts
andEdges
can be private to a package and protected, completely avoiding the prospect of an accidental value assignment ruining things.In Mathematica, it is extremely common to use the
Head
of an expression to identify its type. We can conform to this idiom and redefine our record functions thus:The only material difference between these and the preceding definitions is that the graph object is now represented by an expression of the form
graphRecord[...]
instead of{...}
:Why the change? The first reason is that the head
graphRecord
now positively identifies the type of data whereas before it was just a list. Second, we can define further functions (quasi-methods) that will only act ongraphRecord
s and nothing else. For example:Use:
As a final elaboration to all of this, it would be possible to define a macro function that automatically defined all the record functions given the type and field names. However, as this response is already TL;DR, that is probably best left as topic for another question some day.
Note: If these functions were all defined within the context of a package, their names would use initial capitals (e.g.
MakeGraph
instead ofmakeGraph
). Beware, however, that Mathematica already has lots of built-in symbols that include the wordGraph
.我想知道您使用的 Mathematica 版本是什么?
图论更紧密地集成到 V8 的核心中,这在很大程度上是一个改进。您可以按照面向对象程序员可能喜欢的方式与图形进行交互。在 V8 中,我会像这样执行您的示例:
然后我可以像这样查询属性:
不幸的是,大多数较旧的图论功能在 V8 中不能很好地发挥作用。不过,如果您仔细考虑上下文规范,则可以使用它。在 V7 中,我可能会像这样访问 GraphEdit 的输出:
然后,
获取值得传递给其他函数的内容,例如 GraphPlot。
这部分回答了您的问题,即这是否是合理的表述。这种类型的表示可以轻松地通过替换规则访问信息。 XML 导入的工作方式就是一个很好的例子。
I wonder what version of Mathematica you're using?
Graph theory is more tightly integrated into V8's core, and this is largely an improvement. You can interact with graphs in a way that an object oriented programmer might like. In V8, I'd perform your example like so:
I can then query the property like so:
Unfortunately, most of the older graph theory functionality doesn't play nicely in V8. Although, you can use it if you're careful with context specification. In V7, I might access the output of
GraphEdit
like so:And then,
to get something worth passing to other functions, like
GraphPlot
.This partly answers your question as to whether this is a reasonable representation. This type of representation makes it easy to access the information via replacement rules. A good example of this is given by the way XML import works.
我使用 InputForm[] 找到了问题 2 的答案(今天之前不知道该函数。)
返回
因此,似乎避免问题 1 中的问题的方法以及他们如何定义 GraphEdit 内容的答案是使用规则中的字符串作为“标识符”。
那么问题1可以修改为:这是一个好的做法吗?
I found the answer to question 2 using InputForm[] (hadn't known about that function before today.)
returns
So, it appears the way to avoid the problems in question 1 and the answer to how they define the GraphEdit stuff is to use strings in rules as "identifiers."
Question 1, then can be revised to: is this a good practice?