Movatterモバイル変換


[0]ホーム

URL:


はてラボはてな匿名ダイアリー
ようこそ ゲスト さんログインユーザー登録

「Graphviz」を含む日記RSS

はてなキーワード:Graphvizとは

2025-07-05

生成AIを利用したプログラミング初級者向けの温故知新提案

はじめに

ここで言う「プログラミング初級者」とはプログラミング記述が上から下へ向かって順番に処理されること、条件分岐ループという概念があることを理解しており、RPGゲームが作れる「RPGツクール(現RPG Maker)」や学童向けプログラミング環境Scratch」、「ナビつき! つくってわかる はじめてゲームプログラミング(ナビつく)」、ADVゲームが作れる「吉里吉里(もしくは吉里吉里2)」、過去BASICやC、HSPJavascriptあたりでプログラミングへ挑戦し挫折したなどなど、ある程度の「プログラマブルロジック」構築の経験がある者を指します。

前日談(初級者は読まなくて良いです)

ある時、筆者はふと思いました。「生成AIはなんだかんだで膨大なテキスト情報を処理している事がキモだよなぁ」とありきたりなことを。

そして、同時にプログラミング初級者の弱点として「現在記述されているコード管理においてテキストと実際の処理フロー脳内で一致しない」「プログラミング言語ごとに定められているルール関数予約語の把握が困難」なのが問題とも考えました。

前述したプログラミング初級者の弱点の考え自体車輪の再発明であり、「Scratch」や、より高度な「UML」が既に存在しており、特筆すべきことは何もありません。

しかし、「Scratch」や「UML」、なんなら「RPGツクール」や「吉里吉里」などに無い点として、現代では自然言語処理が大幅に向上した生成AI実用の域にまで到達しつつあるのが従来とは異なる点でした。

まり自然言語を混ぜ込みやすテキストベース言語、かつ、処理を記述するとフロー視覚的に理解やす言語可能であれば情報量が多くて一部の界隈で広く使われている言語があればプログラミング初級者も気軽にプログラミングできるのではないか?と発想しました。

そこで前述の条件を満たす1つの言語へ目を付けました。

本題

コンピュータ(コンパイラインタプリタなどソフトウェアを含む)が解することができる言語にはプログラミング言語以外にも様々あり、今回取り上げるのは「データ記述言語」と呼ばれるものです。

データ記述言語の中でもグラフ作成へ特化しており、特にフローチャート作成で真価を発揮する「DOT言語というものがあります

早速ですが、実際に手を動かしてみましょう。ちなみにDOT言語Graphviz OnlineというWebツールがあるため別途に何かしらをインストールして環境構築する必要はありません。便利な世の中ですね。

上記Graphviz Onlineを開くと、既に左側のDOT言語記述された内容が、右側で作図されています。DOT言語はこのような図を作図するためのデータ記述言語です。

一旦、左側の記述をCtrl+Aで全選択をしDeleteなどで全削除し、下記の内容をコピペしてみましょう。

digraph graphname {

A -> B;

}

一瞬で○に囲まれたAとBが繋がった図が作成されました。

DOT言語の詳細な使い方は様々なWebサイトやブログ記事Qiitaなどへ譲るとして、A - > Bの見た目から発想の転換をしてみると処理Aから処理Bという流れに見えませんか?

DOT言語は生成AIを利用する上で有利なテキストベースでありながらグラフ作成できるのがキモであり、例えばこのA -> BがA「Webページを開いたら」 → B「Hello, Worldと表示する」という風にできるのであれば処理のフロー可視化されており本当に素晴らしいことです。

Hello, worldを表示してみる

ここでプログラミング有識者は「DOT言語UMLなどに見立てて処理を記述するのは良いが、プログラミング初心者は求めた結果を出力するロジックアルゴリズムを発想する知見や経験値が圧倒的に足りていないのが問題ではないか?」と至極真っ当かつ反論余地がない問題点の指摘をすると思いますが、そこで活きるのが生成AIです。

生成AIは初級者プログラマ個人ロジックアルゴリズム作成能力無関係ロジックアルゴリズムを生成できる(可能性が少なくともある)ので、処理のフローをDOT言語記述することにより処理フローから初級者プログラマが求めるプログラムを生成AIへ推論させ生成させるというのが狙いです。実際に試してみましょう。

digraph graphname {

A[
label = "Webページを表示する"
];

B[
label = "Webページを表示したらHello, worldとテキストを表示する"
];

A -> B;

}

プログラミング有識者はこの時点で「なるほど、自然言語記述をそのままDOT言語に入れ込んでしまうのか」と気付いたのではないかと思います

このDOT言語Google Gemini 2.5Flash「下記のデータ記述言語のDOT言語記述された処理フローJavascript言語へ変換をしWebページ上へ「Hello,world」を表示したいので変換をお願いします。」命令すると以下のコードが生成されました。

<!DOCTYPEhtml>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <metaname="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World Page</title>
</head>
<body>
    <divid="output"></div>

    <script>
        //Webページが表示されたら(DOMが完全に読み込まれたら)実行される
        document.addEventListener('DOMContentLoaded', function() {
            // "Webページを表示する"ステップは、このHTMLファイル自体が表示されることで達成されます

            // "Webページを表示したらHello, worldとテキストを表示する"ステップ
            const outputDiv = document.getElementById('output');
            outputDiv.textContent = 'Hello, world';
        });
    </script>

</body>
</html>

フロー記述する利点は、ロジックアルゴリズムを発想する知見や経験値が足りなくとも、フローステップが明確に分かれているので生成AIが処理を切り分けて推論してくれることであり、そしてプログラミング初心者自身フローチャートを視覚確認できるので「Aを処理したらBを処理する」と切り分けて考えやすいことです。

また、求めている結果ではなく誤った結果が生成されても、A - > B - > Cとフローを細分化していくことで生成AIの推論精度を高めていくことができるのも利点です。

応用編

より生成AIへ精度の高い推論をしてもらうために補足情報を付加するのも有用です。

digraph graphname {

A[
label = "Webページを表示する"
];

B[
label = "Webページを表示したらHello, worldとテキストを表示する",
comment = "Webページが完全に読み込まれるまで待機"
];

A -> B;

}

labelの記述内容もcommentの記述内容も生成AIが推論のための情報として利用するので誤った結果が生成されてもA - > B - > Cとフローを細分化しなくとも良い場合があります

DOT言語を知るプログラミング有識者が「DOT言語仕様を考えれば確かにそうだが、その発想はなかった」と言っていただけるであろうDOT言語コード例だとこういう記述方法もアリです。

digraph増田コード {

最初の処理[
label = "Webページを表示する"
];

次の処理[
label = "Webページを表示したらHello, worldとテキストを表示する",
comment = "Webページが完全に読み込まれるまで待機"
];

最初の処理 -> 次の処理;

}

ノード名称自然言語採用することにより、例えばゲームプログラミング時に「キャラクタージャンプする」という読んだそのままな処理のためのノード、というか一般的に言うオブジェクト作成することが可能で、後は->で繋げて処理をさせられます

ちなみに別のノード作成する際に「"キャラクタージャンプする"から継承する」の様なことをcommentなどへ記述しておくと生成AIが推論して継承します。なんならcommentなどへ「キャラクター画像image.gif使用」などと記述しておくとファイルの読み込みもします。

更にDOT言語にはカスタム要素という仕様存在しており、DOT言語仕様で定められた予約語以外も使用可能です。

digraph増田コード {

最初の処理[
label = "Webページを表示する"
];

次の処理[
label = "Webページを表示したらHello, worldとテキストを表示する",
comment = "Webページが完全に読み込まれるまで待機",
font_style = "フォントを太字のボールド体、色を赤(#FF0000)とする"
];

最初の処理 -> 次の処理;

}

生成AIカスタム要素の名称からも推論を発揮し、上記場合であればフォントスタイル指定していると推論をするので生成AIの推論精度を高める補足情報として機能します。

まりこれはカスタム要素の名称として"Action"などの名称採用すると"動作"として推論をし、"decision"ならば"条件分岐"ですし、"input"ならば"入力"ですし、"loop"ならば"繰り返し"ですし、"Type"ならば"種別"です。

より詳細に process[type="Action"] などのノード作成してどんどん生成AIの推論精度を高めていくことが可能であり、そろそろ察してきているかと思いますが 処理[種別="動作"] と自然言語記述しても機能します。

プログラミング有識者は更に「プログラム言語自体予約語、例えばJavascriptを生成する事を前提にlengthを名称にすると配列を使おうとするのか?」と疑問に感じるでしょうがお察しの通りで生成AI配列を使おうとするので、敢えて使いたいプログラム言語機能や外部ライブラリなどがある場合は補足情報として機能する形で記述しておくと生成AIは推論へ利用します(まぁそこまで知識ある方なら該当のプログラム言語使ったほうが手っ取り早いと思いますが)。

おわりに

以上をもって「生成AIを利用したプログラミング初級者向けの温故知新提案」を終えたいと思います

色々とツッコミどころには筆者自身が気付いていて。例えば「結局はDOT言語仕様を覚えないといけないのでは?」とか「プログラミング初級者に任せると生成前のソースであるDOT言語コードスパゲッティになりそうだよな」とか「面倒くせぇから普通にプログラミング覚えろや」とか理解してますし至極真っ当かつ反論余地がないと思ってます

今回の提案プログラミング有識者向けの本質は「生成AIへ向いた中間言語の発掘」であり、「DOT言語ならそこそこ普及してるしプログラミング初級者でも扱えるんじゃね?」と業務中に発想したものを書き留め公開いたしました。

何かプログラミング有識者の皆さんからより良い発想があれば参考にしたいと考えていますのでよろしくお願いいたします。以上。

Permalink |記事への反応(36) | 19:36

このエントリーをはてなブックマークに追加ツイートシェア

2024-12-25

anond:20241225104842

Graphvizあるかな

ただ、フローチャート向けに手懐けるにはわりと工夫がいる。

https://sketchviz.com/flowcharts-in-graphviz

でもやっぱdraw.ioあたりのGUIじゃね?

Permalink |記事への反応(0) | 11:04

このエントリーをはてなブックマークに追加ツイートシェア

2019-01-01

Graphviz を使ってPython抽象構文木を生成する。



1. こんな感じで使います


1.1.スクリプトとして使用する。

$pythonparser.py sample.py


1.2.モジュールとして使用する。

importparsercode ='''a  = 1 + 1print(a)'''graph =parser.create_graph(code)graph.render("sample")


2.ソースコードparser.py はこんな感じです。

importastimport sysimportgraphvizdefcreate_graph(lines):    graph =graphviz.Graph(format='png')root =ast.parse(lines)    node_list = [root]    _setup(graph, node_list)return graphdef_setup(graph, node_list):# node    node = node_list[-1]    node_identity =str(len(node_list))    node_name =type(node).__name__    graph.node(node_identity, node_name)# childrenfor childinast.iter_child_nodes(node):        node_list.append(child)        child_identity =str(len(node_list))        graph.edge(node_identity, child_identity)        _setup(graph, node_list)if __name__ =='__main__':    file_name = sys.argv[1]withopen(file_name)asfile:        lines =file.read()    graph = create_graph(lines)    graph.render(file_name)

Permalink |記事への反応(0) | 02:12

このエントリーをはてなブックマークに追加ツイートシェア

2017-06-21

https://anond.hatelabo.jp/20170621172402

こういうのってはてブを定期的に網曳きして

Graphvizみたいなので可視化してあぶり出してるのだろうか

だとしたら増田日記も一気に消すと過去自演が晒されそうで怖いな

Permalink |記事への反応(0) | 19:29

このエントリーをはてなブックマークに追加ツイートシェア

2007-09-07

ようこそ、℃-uteLisp の世界へ

発祥:http://ex23.2ch.net/test/read.cgi/morningcoffee/1188654905/

はじめに

Scheme というLisp語族言語を用いて℃-ute相関関係プログラムし、様々な角度から関係性を分析する手法を紹介していきます(ソースコードは最後に張ります)。

まずは、メンバー間の関係を「リスト」というデータ型で表現します。例えば「栞菜->愛理」という関係

(kanna . airi)

という形で表すことができます。これに、「大好き」という情報を付加し、ついでにその関係の性質を数値化したものを加えると

((kanna . airi) (desc "大好き") (score . 1))

のようになり、関係図における一つの矢印の情報データ化できたことになります(暫定的に、好意は 1、良好・中立は 0、険悪は -1 の3段階で表すことにします)。

メンバー間の全ての関係性をこのデータ単位で定義し、データベース化しておくことで、色んな条件に基づいた検索やスコア計算などが可能となります。

例 1:リンク状況の調査

ここで相関関係図における矢印を「リンク」と呼ぶことにして、あるメンバーから他のメンバーへどのようにリンクし、またリンクされているかを調べることができます。

関係の中からリンクの起点を抽出してソートしてみると

(sort-nodes (number-list (from-links)))

結果:

((kanna . 6) (saki . 5) (maimi . 4) (erika . 3) (mai . 3) (chisato . 3) (airi . 2))

栞菜ちゃんがメンバー全員にリンクを張っていることが分かり、℃-ute ラブっぷりが伺えます。なっきーにも同様の事が言えます。例の「女の子が好き」発言を数値的に裏付ける結果と言えるかもしれません。

ただ、データ不足でリンク件数がまだ少ないのと、リンクの性質(好意/反感など)までは分からない点を考慮する必要があるでしょう。

例 2: 被リンク状況の調査

同様に、リンクの終点の件数を調べてみます。

(sort-nodes (number-list (to-links)))
((chisato . 5) (erika . 5) (kanna . 4) (maimi . 4) (airi . 4) (mai . 3) (saki . 1))

えりかちゃんと千聖ちゃんが高ポイントです。メンバーからの人気や注目度の高さを示すデータですが、千聖ちゃんの場合敵対的なリンクが2件含まれている点に注意してください。

なっきーの被リンク数が極端に少ないですが、単純にデータ不足のためだと思われます。はぶら(ryとか言わないようにお願いします。

例 3: 愛情度の評価

リンクに付随するスコアを計算することで、愛情の度合いを測ることができるのではないか、という考えに基づく研究です。

まず、全ての関係性を対象として、スコアマイナス関係を抽出してみます。

(filter-nodes (lambda (n)(< (score-relation n) 0)))

結果:

(((kanna . chisato) (desc "愛理に手出すんじゃねぇよ") (score . -1)) ((saki . chisato) (desc "愛理に手出すんじゃねぇよ") (score . -1)))

件数だけを得ると

(length (filter-nodes (lambda (n)(< (score-relation n) 0))))
2

僅か2件です。

良好・中立的な関係

(length (filter-nodes (lambda (n)(= (score-relation n) 0))))
8

愛に満ちた関係

(length (filter-nodes (lambda (n)(> (score-relation n) 0))))
16

非常に多いです。舞美ちゃんの「℃-ute同士でラブラブなんですよ」発言(例のラジオ)を数値的に裏付ける結果と言えるんじゃないでしょうか。

次に、メンバーごとのスコアを算出してみます。Lisp 的には以下のようにフィルタリングと畳み込み (fold) で計算することができます。例えば

(foldr (lambda (n acc) (+ (get-score n) acc))       0       (filter-nodes (cut to? <> 'kanna)))

栞菜ちゃんに対するリンクスコアが得られます。結果:

3

上式を一般化して一挙にメンバー全員に適用してみると

(sort-nodes (map (lambda (x)   (cons x (score-loved x))) (all-members)))

結果:

((airi . 4) (kanna . 3) (mai . 2) (erika . 2) (maimi . 2) (saki . 1) (chisato . 0))

愛理ちゃんが好意を寄せられやすい傾向が伺えます。

今度は逆方向のスコアを計算してみると

(sort-nodes (map (lambda (x)   (cons x (score-loving x))) (all-members)))
((kanna . 3) (maimi . 3) (chisato . 2) (airi . 2) (saki . 2) (mai . 1) (erika . 1))

まいまいえりかちゃんが特に堅い・一途だという傾向を読み取ることができます。

例 4: 相性の調査

今度は組み合わせ(カップリング)の評価です。

2点間相互のリンクスコアを加算したものを「相性」と考えられるものとします。最大値 (互いに好意を寄せている場合の数値) は現在スコアリング方式では 2 です。例えば

(score-between 'kanna 'airi)

の値は

2

となります。1 であれば一方通行と考えます。

関係性が未定義の場合もあるので 0 のものを除外して算出すると

(sort-nodes (filter (lambda (n)      (not (= (cdr n) 0)))    (map (lambda (n)   (cons n (apply score-between n))) (all-combinations))))
(((chisatomai) . 2) ((chisato airi) . 2) ((airi kanna) . 2) ((saki kanna) . 2) ((kanna maimi) . 2) ((erika maimi) . 2) ((saki airi) . 1) ((sakierika) . 1) ((kannamai) . 1) ((maimi airi) . 1) ((saki chisato) . -1) ((kanna chisato) . -1))

となります。若干ピンとこない部分もあるかも知れませんが、計算上は矛盾無くデータの内容を表しています。

参考までに、スコア 1 の相互関係の中身を見てみると

(map (lambda (p)       (find-relation (cons (caar p) (cadar p))      identity))     (filter (lambda (n)       (= (cdr n) 1))     (map (lambda (n)    (cons n (apply score-between n)))  (all-combinations))))
(((kanna .mai) (desc "喰ってやるよ") (score . 1)) ((saki . airi) (desc "好き") (score . 1)) ((maimi . airi) (desc "良き妹") (score . 1)) ((saki .erika) (desc "彼氏にしたい") (score . 1)))

のようになります。

まとめ

以上の調査を経て気になった問題点を列挙してみます。

特に最初の点に関して、「百合的」なるものの質的評価がなかなか難しいと感じました。例えば「大好き」も「良き妹」も同じ 1 と評価してしまっているのが妥当かどうか、といったことです。

また、スレにて与えられた情報を評価・分析する方法としては有効だとしても、逆方向のフィードバックの手段がなかなか見つからないというのが三つ目の問題です(技術力不足とも言います)。(注:画像化の方法が分かりました。追記参照)

最後に、プログラムソースを示します。実行にはPLT Scheme が必要です。文字コードUTF-8 で保存した上で、(load "c-ute.ss") としてください。文字化けする場合はターミナルUTF-8 を表示できるよう設定する必要があります。がんばってください。

プログラム

c-ute.ss:

(require (lib "etc.ss")         (lib "list.ss")         (lib "26.ss" "srfi")         (lib "delete.ss" "srfi" "1"));;; Utilities(define true? (compose not not))(define (ignore _) #f)(define fif  (case-lambda    ((predicate consequent)     (fif predicate consequent ignore))    ((predicate consequentalternative)     (lambda (x)       (if (predicate x)           (consequent x)           (alternative x))))))(define (concat! xs) (apply append! xs))(define (mapconcat f lst sep)  (letlp ((str (f (car lst)))           (lst (cdr lst)))    (if (null? lst)str        (lp (string-appendstr sep (f (car lst)))            (cdr lst)))))(define (slice-stringstr len)  (letlp ((res '())           (strstr))    (if (<= (string-lengthstr) len)        (reverse! (consstr res))        (lp (cons (substringstr 0 len) res)            (substringstr len)))))(define (break-stringstr len)  (mapconcat identity (slice-stringstr len) "\\n"));; NOTE: input and outputports have to be either file-stream or #f;; (i.e., cannot be astring port)(define (run exe opt in out)  (let-values (((p p-i p-o p-e)                (subprocess out in #f exe opt)))    (subprocess-wait p)    (close-input-port p-e)));;; Database;; http://ja.wikipedia.org/wiki/%E2%84%83-ute(define names  '((erika . "えりか") (maimi . "舞美") (saki . "早貴") (airi . "愛理")    (chisato . "千聖") (mai . "舞") (kanna . "栞菜")))(define (symbol->name sym)  ((fif true?        cdr)   (assq sym names)))(define nodes '())(define edges '())(define (relate from to desc score)  (let ((n (cons from to)))    (or (find-relation n                       (lambda (r)                         (let ((d (assq 'desc r))                               (s (assq 'score r)))                           (set-cdr! d (cons desc (cdr d)))                           (set-cdr! s (+ score (cdr s))))))        (begin          (set! nodes (cons n nodes))          (set! edges (cons (cons n `((desc ,desc)                                      (score . ,score)))                            edges))))))(define (find-relation n k)  ((fif true? k)   (assoc n edges)))(define (related? x y)  (find-relation (cons x y) (lambda (_) #t)))(define (from? n x)  (eq? (car n) x))(define (to? n x)  (eq? (cdr n) x))(define flip-relation  (case-lambda    ((n)     (and (related? (cdr n) (car n))          (cons (cdr n) (car n))))    ((n k)     ((fif true? k)      (flip-relation n)))))(define (get-score n)  (cdr (assq 'score n)))(define (get-description n)  (cdr (assq 'desc n)))(define (describe-relation n)  (find-relation n get-description))(define (score-relation n)  (or (find-relation n get-score) 0))(define (print-node . ns)  (for-each (cute find-relation <>                  (lambda (r)                    (display                     (format "| ~a => ~a  (~a)~%"                             (caar r) (cdar r)                             (mapconcat (lambda (s)                                          (string-append "\"" s "\""))                                        (cdr (assq 'desc r))                                        ", ")))))            ns))(define (iter-nodes k)  (letlp ((nodes nodes))    (unless (null? nodes)      (k (car nodes))      (lp (cdr nodes)))))(define (filter-nodes p)  (let ((ns '()))    (iter-nodes (fif p                     (cut find-relation <> (lambda (n)                                             (set! ns (cons n ns))))))    ns))(define (from-links)  (map car nodes))(define (to-links)  (map cdr nodes))(define (all-members)  (delete-duplicates! (from-links)))(define (all-pairs) nodes)(define (ordered-pairs)  (concat! (map (lambda (x)                  (map car                       (sort (filter-nodes (cute to? <> (car x)))                             (lambda (x y)                               (> (get-score x) (get-score y))))))                (sort-nodes (map (lambda (x)                                   (cons x (score-loved x)))                                 (all-members))))))(define (all-combinations)  (letlp ((cs '()) (ns nodes))    (if (null? ns)cs        (let ((n (car ns)))          (lp (if (member (list (cdr n) (car n))cs)cs                  (cons (list (car n) (cdr n))cs))              (cdr ns))))));;number-list :: [a] -> [(a . Int)](define (number-listls)  (letlp ((ns '()) (lsls))    (if (null?ls)        ns        (let ((x (carls)))          (lp ((fif not                    (lambda (_) (cons (cons x 1) ns))                    (lambda (n)                      (set-cdr! n (add1 (cdr n)))                      ns))               (assq x ns))              (cdrls))))));; sort-nodes :: [(a . Int)] -> [(a . Int)](define (sort-nodes ns)  (sort ns (lambda (x y)             (> (cdr x) (cdr y)))))(define (diff-nodesms ns)  (letlp ((ds '()) (ns ns))    (if (null? ns)        (sort-nodesds)        (lp (let* ((n (car ns))                   (m (assq (car n)ms)))              (cons (cons (car n)                          (- (cdr m) (cdr n)))ds))            (cdr ns)))))(define (get-total-score x p)  (foldr (lambda (n acc)           (+ (get-score n) acc))         0         (filter-nodes (cut p <> x))))(define (score-loved x)  (get-total-score x to?))(define (score-loving x)  (get-total-score x from?))(define (score-between x y)  (+ (score-relation (cons x y))     (score-relation (cons y x))))(define (-> x)  (display (format "~%Links from [~a]~%" x))  (iter-nodes (fif (cut from? <> x)print-node)))(define (<- x)  (display (format "~%Links towards [~a]~%" x))  (iter-nodes (fif (cut to? <> x)print-node)))(define (<-> x)  (display (format "~%Reciprocal links for [~a]~%" x))  (iter-nodes (fif (cut to? <> x)                   (lambda (n)                     (flip-relation n                                    (lambda (m)                                      (print-node m n)))))))(define (<=> x)  (display (format "~%Reciprocal matches for [~a]~%" x))  (iter-nodes   (fif (cut to? <> x)        (lambda (n)          (flip-relation n                         (lambda (m)                           (if (ormap (lambda (x)                                        (ormap (lambda (y)                                                 (equal? x y))                                               (describe-relation m)))                                      (describe-relation n))                               (print-node m n))))))))(define (<?> x)  (let ((to (assq x (number-list (from-links))))        (from (assq x (number-list (to-links)))))    (display (string-append              (format "~%Link statistics for [~a]~%"                      x)              (format "| ~a => ~a (love ~a)~%"                      x                      (cdr to)                      (score-loving x))              (format "| ~a => ~a (love ~a)~%"                      (cdr from)                      x                      (score-loved x))))))(define (info x)  (for-each (cut <> x)            (list <- <-> <=> -> <?>)));;;GraphViz (http://www.graphviz.org/) support(definegraphviz "C:/Program Files/ATT/Graphviz/bin/dot.exe")(define (nodes->dot ns)  (string-append "digraph cute {\n"                 ;;"\tordering=out;\n"                 ;;"\trankdir=LR;\n"                 "\toverlap=true;\n"                 "\tnode[fontname=\"msgothic.ttc\"];\n"                 "\tedge[fontname=\"msgothic.ttc\",fontsize=9];\n"                 (letlp ((str "") (ns ns))                   (if (null? ns)str                       (let* ((n (car ns))                              (s (score-relation n)))                         (lp (string-appendstr                              (format "\t\"~a\" -> \"~a\""                                      (symbol->name (car n))                                      (symbol->name (cdr n)))                              (format "[label=\"~a\",color=\"~a\","                                      (break-string                                       (car (describe-relation n))                                       7)                                      (cond ((> s 0) "red")                                            ((= s 0) "green")                                            (else "blue")))                              (format "style=\"bold~a\"];\n"                                      (if (and (not (= s 0)) (< s 1) (> s -1))                                          ",dashed"                                          "")))                             (cdr ns)))))                 "}"))(define (write-dotfile dot file)  (and (file-exists? file) (delete-file file))  (with-output-to-file file    (lambda ()      (display dot)))  file)(define (dot->png dotpng)  (call-with-input-file (write-dotfile dot "c-ute.dot")    (lambda (in)      (and (file-exists?png) (delete-filepng))      (call-with-output-filepng        (lambda (out)          (rungraphviz "-Tpng" in out)))))  'done);;; Setup database;; Based on:;; http://ex23.2ch.net/test/read.cgi/morningcoffee/1188654905/116-142(begin  (relate 'maimi 'erika "大好き" 1)  (relate 'maimi 'kanna "良き妹" 1)  (relate 'maimi 'airi "良き妹" 1)  (relate 'maimi 'mai "姉妹" 0)  (relate 'erika 'maimi "一番可愛いよ" 1)  (relate 'erika 'kanna "仲間" 0)  (relate 'erika 'chisato "おソロパジャマ" 0)  (relate 'kanna 'erika "仲間" 0)  (relate 'kanna 'maimi "好き" 1)  (relate 'kanna 'saki "喰ってやるよ" 1)  (relate 'kanna 'mai "喰ってやるよ" 1)  (relate 'kanna 'airi "大好き" 1)  (relate 'kanna 'chisato "愛理に手出すんじゃねぇよ" -1)  (relate 'saki 'maimi "荷物整理" 0)  (relate 'saki 'erika "彼氏にしたい" 1)  (relate 'saki 'kanna "興味がある" 0.5)  (relate 'saki 'chisato "愛理に手出すんじゃねぇよ" -1)  (relate 'saki 'airi "好き" 1)  (relate 'airi 'kanna "受け入れる" 1)  (relate 'airi 'chisato "最近親密" 1)  (relate 'mai 'erika "保護者" 0)  (relate 'mai 'maimi "姉妹" 0)  (relate 'mai 'chisato "恋人" 1)  (relate 'chisato 'erika "おソロパジャマ" 0)  (relate 'chisato 'mai "恋人" 1)  (relate 'chisato 'airi "最近親密" 1));; query relations /draw graphs(if (file-exists?graphviz)    (dot->png (nodes->dot (ordered-pairs))              "c-ute.png")    (for-each info (all-members)))

追記(グラフ描画について)

Graphviz というソフトによって関係図を可視化できる、ということを教えていただきました(既に上プログラムを実行すると自動的に関係画像を作成するようにしてあります)。ここでは技術的な観点から幾つか注意点を挙げておきます。

まず、SchemeプログラムからGraphviz を動かす方法について。コマンドラインからの起動のように、プログラムへのオプション文字列で入出力ファイルを指定する方法ではどうも上手く行きませんでした。調査の結果、入出力ファイルポートScheme 側で用意しておく必要があるようです。処理系によって異なりますが、PLTScheme の場合 subprocess という関数を次のように呼び出します。

(subprocess output-port input-port #f "/path/to/dot.exe" "-Tpng")

ここで output-port はpng画像ファイルへの出力ポート。input-port は dotファイルグラフの定義ファイル)の入力ポートです。エラーポートは必要無いでしょう (#f)。

dot という名前の実行ファイルが、関係図のような有向グラフを描画するプログラムです。最後にオプション文字列として出力形式を指定します(png,jpeg,gif,etc.)。

次に dotファイルScheme で書く方法ですが、以下の基本的な有向グラフの書式

digraph g {  A -> B;  B -> C;  C -> A;}

を理解すれば、後は実直にSchemeデータを当てはめて format関数等で変換するだけです。

(string-append "digraph g {" (format "~a -> ~a;" (car node) (cdr node)) "}")

問題は、ノードを配置する順番によって出来上がる画像が変わってくる、ということです。

より見た目に分かりやすくするための工夫としては、相互にリンクするノード同士が dotファイル上でも近接して出力されるようにすると良いでしょう。関連の強いものが画像の上でも近くに表示されるようになります。

また上述(特に例3)のスコア概念を応用し、スコアの低いものが後に出力されるようにすることで、重力感覚に一致するような関係図を得ることができるでしょう。

Permalink |記事への反応(0) | 17:26

このエントリーをはてなブックマークに追加ツイートシェア

 
ログインユーザー登録
ようこそ ゲスト さん
Copyright (C) 2001-2025 hatena. All Rights Reserved.

[8]ページ先頭

©2009-2025 Movatter.jp