В чем разница между правилами Mathematica и объектами, возвращаемыми GraphEdit? - PullRequest
1 голос
/ 27 июля 2011

Это на самом деле двойной вопрос. Во-первых, как кто-то, имеющий опыт программирования ОО, я нахожу, что использование 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. Так что, черт возьми, это г [[2]] [[1]]?

Похоже, что это настоящий идентификатор, который я хотел бы использовать для решения проблем с вопросом 1 выше. Кто-нибудь знает разницу, или как напечатать одно против другого в Mathematica?

Я не могу найти ничего в документации об этом (или в Интернете). Спасибо.

Ответы [ 3 ]

4 голосов
/ 27 июля 2011

Интересно, какую версию 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.

4 голосов
/ 29 июля 2011

Правила 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) символам с начальными буквами верхнего регистра.Вольфрам префикс всех переменных верхнего уровня с $, например, $Context.Если вы будете придерживаться этих соглашений, вы получите некоторую меру безопасности.

Во-вторых, есть положение для отдельных пространств имен, использующих Packages .В пределах определяемого вами пакета вы можете полностью контролировать привязки символов, которые вы используете в качестве имен полей.

В-третьих, вы можете использовать Защитить , чтобы имена полей не имелиприсвоенные им значения.

При реализации этих типов записей можно следовать идиоме 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 с и ничего больше.Например:

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$ Aborted

В качестве окончательной проработки всего этого можно было бы определить макрофункцию, которая автоматически определяет все функции записи, учитывая тип и имена полей.Однако, поскольку этот ответ уже является TL; DR, его, вероятно, лучше всего оставить в качестве темы для другого вопроса.

Примечание. Если бы все эти функции были определены в контексте пакета, их имена будут использовать начальныестолицы (например, MakeGraph вместо makeGraph).Однако помните, что в Mathematica уже есть много встроенных символов, включающих слово Graph.

0 голосов
/ 27 июля 2011

Я нашел ответ на вопрос 2, используя InputForm [] (не знал об этой функции до сегодняшнего дня.)

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

возвращает

"Graph"

Итак, похоже,способ избежать проблем в вопросе 1 и ответ на то, как они определяют материал GraphEdit, заключается в использовании строк в правилах в качестве «идентификаторов».

Вопрос 1, а затем может быть изменен на: это хорошая практика?

...