1

これは実際には 2 つの質問です。最初に: 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 データ構造を作成するための良い方法ですか、それとも悪い方法ですか? このようにしている理由の 1 つは、色などの任意のプロパティをアタッチできるようにするためです。

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

私の関数は、特定のプロパティが追加された正確な順序を知る必要はありません。たとえば、次のように定義された関数 GetColor があるとします。

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

色情報を持つグラフデータ構造を常に持ちたいとは限らず、色情報のためにリスト内のスポットを予約したくないので(グラフ[[[3]]]のように)、これが望ましいです。

2 つ目: 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 に入力する方法はありますか?

これに関するドキュメント(またはオンライン)には何も見つかりません。ありがとう。

4

3 に答える 3

4

GraphEditルール

GraphEditGraphics最初の要素がオブジェクトで、残りの要素がグラフを説明するルールであるリストを返します。各ルールの左側は文字列であり、記号ではありません。これは、を使用して判別できますg // FullForm。グラフルールを抽出するには、リストの最初の要素を無視する必要があります。

"Graph" /. Drop[g, 1]

レコードタイプのシミュレーション

提案するように、レコードのようなデータ型を実装することは合理的なアプローチです。

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

Vertsとに値が割り当てられた場合、Edges「奇妙な副作用」が発生することは事実です。ただし、その問題を軽減する方法はいくつかあります。

OwnValuesまず、Mathematicaには、大文字の頭文字を持つ記号に値(具体的には)を割り当てないようにするための非常に普及した規則があります。Wolframは、すべての最上位変数の前に$、たとえば、を付け$Contextます。これらの規則に固執すれば、ある程度の安全性が得られます。

次に、 Packagesを使用して個別の名前空間を用意します。定義するパッケージの範囲内で、フィールド名として使用するシンボルのバインドを完全に制御できます。

第3に、保護を使用して、フィールド名に値が割り当てられないようにすることができます。

これらのレコードタイプを実装する場合、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をパッケージにプライベートにして保護することができ、偶発的な値の割り当てによって物事が台無しになる可能性を完全に回避できます。

HeadMathematicaでは、式のを使用してその型を識別するのが非常に一般的です。このイディオムに準拠し、レコード関数を次のように再定義できます。

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への無効な引数:こんにちは
$ Aborted

これらすべての最終的な詳細として、タイプとフィールド名を指定してすべてのレコード関数を自動的に定義するマクロ関数を定義することができます。ただし、この応答はすでにTL; DRであるため、いつか別の質問のトピックとして残すのがおそらく最善です。

注:これらの関数がすべてパッケージのコンテキスト内で定義されている場合、それらの名前は(たとえば)MakeGraphの代わりに最初の大文字を使用しますmakeGraph。ただし、Mathematicaにはすでに単語を含む多くの組み込み記号があることに注意してくださいGraph

于 2011-07-29T03:12:53.573 に答える
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 インポートの仕組みです。

于 2011-07-27T18:26:16.057 に答える
0

InputForm[] を使用して質問 2 の答えを見つけました (今日までその関数について知りませんでした)。

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

戻り値

"Graph"

したがって、質問 1 の問題を回避する方法のように見えます。また、GraphEdit の定義方法に対する答えは、ルール内で文字列を「識別子」として使用することです。

質問 1 は次のように修正できます。

于 2011-07-27T17:36:25.000 に答える