「」に属する記事(最新5件のみ展開表示)

メイン

2007年02月24日

GRINEditを使ってソースコードの可視化

控えめな Brainfuck コードを色づけする GM < 04 < September < 2006 < nulog, NULL::something : out of the headphoneを見て、唐突に思いついたので作ってみました。

可視化する対象のコードは、 竹迫さんのTAKESAKO @ Yet another Cybozu Labs: Brainf*ckで100までの素数を列挙してみるテストと、奥さんのKazuho@Cybozu Labs: brainf*ck でマジメに素数探索

まず、竹迫さんのコードの全体像。

そして一部拡大した物がこれ。 各頂点が1つの命令で、太い辺が命令の隣接関係、 細い辺が括弧の対応関係です。+が赤、-が青、.が緑になっています。

竹迫さんのコードは実は数字を作って表示するだけのコードなので、 大局的な構造がありません。 大まかに見ると「小さなループが所々にある1本の紐」です。 可視化する際も、頂点を徐々にy座標を増やしながら作るだけでこんな風に ほとんどもつれずに表示されました。

いっぽう奥さんのコードはこんな感じ。

ぱっとみで、竹迫さんのコードより緑の頂点が少ないことに気づきます。

一部拡大。

括弧のネストがあるために、複雑な構造をしています。

少し引っ張って見たのがこちら。 赤い頂点(+)が、かなり局在していることがわかります。

赤丸で囲んだ部分を拡大したのが次の図。

実はこの部分にしか緑の頂点がありません。 つまりこの固まりが「表示ルーチン」に相当する物だと考えられます。


= コードはPythonでXML-RPCを使って個々の頂点を投げる物で、 コード中に直接貼り付けたBrainf*ckのコードを除けば 50行程度。 GRINEditの次のリリースの時にでもサンプルの中に入れます。

思ったこと。 反発力を使った整形は、 こういう「長いヒモ」状の構造を整形するのにはあまりむいていません。 また、かりに十分時間をかけて整形させたとして、竹迫さんのコードのうねうね曲がっているヒモが、 一直線に伸びたら、それは本当に見やすい可視化なのだろうか?と言われると多少疑問です。 適切に折りたたまれている方が見やすいです。

竹迫さんのコードを適切に折りたたんで可視化するのは難しくないですね。 例えば20個おきの頂点に「左右に引っ張る力」を足してやれば、自然と折りたたまれるはずです。

問題は奥さんのコードの方です。 大局的な構造があるために、竹迫さんのコードを可視化する際に使った 「頂点の初期位置を順番にずらす」という方法ではきれいになりません。 かといって今の手動整形では頂点の移動しかできないので、つまんで引っ張っても 「隣の頂点が半分くらいの速度で付いてくる+ その隣の頂点がさらに半分の速度で付いてくる+ その隣の頂点がさらに半分の速度で付いてくる…」 となって、いまいちです。

これは今まで考えなかったわけではないのですけど、 いい「整形しづらさを感じるグラフ」が手元になかったので対策していませんでした。 これに対処する方法は、一つは前処理です。 ある種の性質を持っているグラフには、 見やすく表示することができる「静的なレイアウト」があります。 例えば今回の場合は平面グラフなので、 辺が交叉しないように平面に埋め込むアルゴリズムがあります。 実はそういう静的な処理を動的な整形の前に挟む機能がすでにGRINEditに実装されています。 今は「非連結グラフを連結成分ごとにばらして配置する」っていうシンプルな物だけですけども、 「平面グラフを交叉しないように埋め込む」っていう前処理は汎用性も高いので 用意しておくのもいいですね。

もう一つは、よりリッチな動的整形手法を作ること。 こちらの方がどちらかというと萌えます。 例えば頂点でマウスダウンした際に、 その頂点が紐の一部であることの判定や、 紐を構成する頂点を取得することは難しくありません。 そこで紐をドラッグ中だけ一時的に剛体化してしまうこともできます。 使い勝手は実装して試してみないとわかりませんが。

他のアイデア: 辺の間に角度バネをつけると、 反発力が届きにくいような長い構造でもきれいに伸びる、かも。 紐状の部分を一時的に1つの頂点に置き換えてしまう。 ソースコードのフォールディングみたいな感じ。 必要なときにダブルクリックすれば元通りに展開されるとか。 n本の辺からなるパスを自然長nの辺で置き換えてみると、 すばやく大局的な整形が行われる、かも。

Non-coding RNAs: Transfer-RNA (tRNA)

2006年12月06日

人は言葉のみで考えているのか?まさか!

404 Blog Not Found:「はじめに言葉ありき」は本当か?で書かれていることはまさに僕が思っていることと同じで、 同じように考える人が他にもいることを知ってちょっと安心しました。 最近西尾泰和のブログ: ESPer2006の懇親会の3次会で語った内容で 「言語というものは脳の中のものをシリアライズして通信するためのもので、 技術は進歩したのだから古い通信方法にこだわっていないで新しい方法を模索してもよいのでは」ということを書いたばかりだったのでちょっとびっくりしました。
少なくとも、私の考えは「文字列」ではない。もっと多次元の「もやもや」だ。私にとって「言葉」とはあくまでそれを「シリアライズ」 (serialize)したもので、それを聞くということはいちいちそれを「デシリアライズ」(deseriarize)して、構文解析(parse)して....というまだるっこしい作業を伴うものだ。少なくとも「直感」とはほど遠い。
どうしてこういう「まどろっこしいこと」が普及してしまったかというと、 結局は言語を発達させた時代の人類が、 通信手段として音声という一次元的なハードウェアしか使えなかったからだろうと思います。 (もちろんその時代でも絵で表現するという方法があっただろうけど、 絵で表現するには道具が必要で、 紙のような軽くて持ち運びやすいメディアもなかったのでコストが高かった。)

もし我々がサルではなくイルカから進化したのなら、 超音波を使って3次元形状を直接やりとりできたかもしれません。 哺乳類にならずに爬虫類から直接知性を持つに至ったなら、 皮膚の色の変化を使って2次元イメージを直接伝えていたかも知れません。

プロ棋士が考えているときにどの程度言葉を使って考えているのか、 彫刻家が彫刻をしているときにどの程度言葉を使って考えているのか、 プログラマがコーディングをしているときにどの程度言葉を使って考えているのか、 100%言葉で考えているわけではないと思います。 少なくとも自分がコーディングをしているときには 言語的でない思考が結構多いと感じています。

英会話ではよく「英語で考えろ」と言いますが、 これは適切な表現ではないと思います。 日本語で考えているわけではなく、考えたことを日本語で出力しているのです。 英語で出力するときに、考えたことを日本語で出力してから記号操作で英語に変換しようとすると 処理速度が間に合いません。人間の言葉を使った記号処理能力はさほど高くないですから。 そこで考えたことを直接英語で出力するように訓練するわけです。 同じことで、Pythonでコーディングしているときには考えたこととPythonで出力しているだけで 別に日本語で考えたりPythonで考えたりしているわけではないです。 僕の実感としては「思いついたことを言葉にしてF5キーを押すと返事が返ってくる」に近いです。 言葉で考えた結果をコードにするのではなく、考えたことを表現した結果がコードという言葉なのです。

ESPer2006の懇親会の3次会で語った内容

ESPer2006の懇親会の3次会で語った内容を思い出しつつ次の日に書いたものを 西尾泰和のブログ: エスパー日記 から抽出。

GRINEditが目指しているものは、 グラフの可視化ソフトだ、 と説明するのが世間的には理解しやすいのだろう。 インタラクティブで、フォーマットが腐っていなくて、 拡張性の高いものを作ろうとしている、と説明するのが受け入れられやすい説明だろう。 しかし、それは第一コーナーであって、ゴールじゃない。 世の中の自然言語はきっと全て一次元的だが、 それはその自然言語が発生する過程で一般的だった声&耳という通信デバイスが 一次元的な記号列の通信に適していたからに過ぎない。 まずは音声に記号列を乗せて通信する方法が発明され、 それからそれを永続化する手法として文字が発明された。 紙の発明によって記号列の保管コストが下がった。 活版印刷が発明され、記号列の生成コストが下がった。 コピー機が発明され、記号列の複製コストが下がった。 電信の発明により、記号列を配信するコストが下がった。 そしてご存じの通り、コンピューターが現れ、 インターネットが現れ、すさまじいコストの低下をもたらした。 そしてその時代に、人間が自分の脳内で作り出された情報を他者に通信する手段は 旧態依然の一次元の記号列を使っている。 これはIDEが使える時代なのにハンドアセンブルでプログラムを書いているようなものなのではないか。 ハードディスクが使える時代なのにパンチカードを使うようなものではないか? 僕はそこをなんとかしたいんだ!

というようなことを話したような気がする。

質疑応答。「Q:君がやろうとしていることは人間を進化させることか?」 A:携帯電話の普及により、多くの人が遠隔地の人間とコミュニケーションする能力を獲得した。 これを進化と呼ぶのなら、僕がやろうとしていることももちろん進化だろう。

他にもチューリングテストやバベルの塔、 記号と確率とどちらが根源的か、なんて感じで色々盛り上がったのだけど、 どういう流れでどういう話だったか説明できるほどには覚えていない。

でもまあ、僕は「記号こそ本質で確率はまやかし」という意見には同意できないと思った。 人間が生得的に持っている素朴物理学が、実際の物理学とは一致しないのと同様に、 人間が生得的に持っている観測デバイスにも性能の限界があり、 外界を正確には観測できていない。 人間は、実際には分布の標準偏差が小さいだけなのに 勝手にデルタ関数だと思いこんだり (ディラックのデルタ関数 - Wikipedia)、 メンバシップ関数の値がほとんどの場合において1か0であるというだけの理由で それをクリスプな集合だと思いこんだりする。(ファジィ - Wikipedia)

あるコンピュータのCPUがポンコツで、実数値をたかだか十数桁の精度でしか扱えない、「考える際には丸めないといけない」ものだったからといって、実数という概念より浮動小数点数という概念の方が本質的かというとそうではない。 実数の方が、浮動小数点数よりも、よりシンプルでパワフルな概念だ。 ハードウェアがポンコツなせいで実装が浮動小数点数でなければいけないとしても、理想的なモデルは実数であるべきだ。 実数の概念なしで物理学を構築したら、バッドノウハウだらけのゴミの山が築かれ、いずれ行き詰まる。クォータニオンなしで3Dのプログラムを書いたら、いずれ行き詰まる。 狭き門より入れ。

同じことで、人間が一旦記号列に落とさないと他人とコミュニケーションしながら考えたり、 考えたことをシリアライズしておいて後で再開したりすることができない、というのは 人間というハードウェアがポンコツなだけだ。 それのポンコツハードウェアにとらわれていてはいけない。 実際、芸術家や囲碁棋士は記号列を使わない思考方法が可能であることをよく示していると思うし、 自分自身も記号列ではない思考を行っていると思う。

2006年12月03日

GRINEditの頂点をMeadowで編集

西尾泰和のブログ: howmで書く日記4日目西尾泰和のブログ: MeadowからGRINEditを操作する で紹介した要素を組み合わせれば理屈の上ではできると思っていたのですが、 やってみたらやっぱりできました。

デモ

原理は、

  • GRINEditでダブルクリックをしたときに
    • 最寄り頂点のIDとラベルを取得
    • "grinedit"バッファを開いてIDとラベルを書き込むEmacs Lispコードを生成
    • gnuservを使ってMeadowに送信
    • Meadowに頂点IDやラベルが表示される
  • Meadowでラベルを編集
  • あらかじめ定義してある関数を実行
    • Pythonで書かれたスクリプトを起動する
    • カレントバッファの内容を標準入力に流し込む
    • スクリプトがXML-RPCでGRINEditを操作する

ダブルクリックをしたときに行う処理の登録は プロトタイプとしてinitMouseMeadiator.pyに直接下のように書きました。

class MO_OpenMeadowWhenDoubleClicked(mo.MouseOperation):
    def mouseDoubleClick(self, x, y, i):
        target = med.getNearestVertex(x, y);
        if target:
            label = target.getParams().get("label")
            print label
            self.openMeadow(target.getId(), label)
    
    def openMeadow(self, vertexId, contents):
        import os
        sexp = r'(switch-to-buffer \"grinedit\")'
        sexp += r'(erase-buffer)'
        sexp += r'(insert \"%s\n\")' % vertexId
        s = contents.replace("\n", r"\n")
        sexp += r'(insert \"%s\")' % s
        cmd = "gnudoit -F \"%s\"" % sexp
        print cmd
        os.system(cmd)

mm.add(_("Test_OpenMeadow"), MO_OpenMeadowWhenDoubleClicked())

os.systemを使ってgnudoitを起動しているわけです。 -Fオプションをつけているのに Meadowにフォーカスがうつらないのは謎。

Meadow側では.emacsに下のようにキーバインドの定義が書かれています。

(defvar GRINEDITPY "c:\\meadow\\grinedit_modVertex.py"
  "*The command to send query to grinedit")

(defun grinedit ()
  (interactive)
  (call-process-region 
   (point-min)
   (point-max)
   GRINEDITPY))

(define-key global-map "\C-cg" 'grinedit)

ようはgrinedit_modVertex.pyを起動し、 バッファの最初から最後までを標準入力に流し込んでいるわけです。

起動されているgrinedit_modVertex.pyの中身は以下。

import xmlrpclib, sys
text = sys.stdin.read()
items = text.split("\n", 1)
vertexId = items[0]
contents = items[1]
server = xmlrpclib.Server("http://localhost:8080/RPC2")
g = server.grinedit
g.modVertex(vertexId, {"label": contents})

改行で分割して、1行目を頂点ID、2行目以降をラベルの値として、 頂点のパラメータを変更するクエリを投げています。

やってみた感想としては、 Meadowが直接XML-RPCのサーバやクライアントになってくれるといいのにな…ということでしょうか。 一応目的のことは実現できてはいるものの、 なんか泥臭いなぁ…と。

2006年11月28日

MeadowからGRINEditを操作する

odz buffer - coLinux で Emacs の kill-ring の内容をWindowsのクリップボードと同期するを参考に、とりあえず試してみました。 単に選択範囲の文字列をGRINEdit上に出すだけですが。

使用前

使用後

Emacs側

(defvar GRINEDITPY "grinedit.py"
  "*The command to send query to grinedit")

(defun grinedit (beg end)
  (interactive "r")
  (call-process-region 
   beg 
   end 
   GRINEDITPY))

(define-key global-map "\C-cg" 'grinedit)
call-process-regionでgrinedit.pyを呼んでいます。 選択されたリージョンは標準入力に流し込まれているみたいでした。 環境によって違ったりするのかも知れません。EmacsLispは詳しくないので。

grinedit.py

import xmlrpclib, sys
text = sys.stdin.read()
server = xmlrpclib.Server("http://localhost:8080/RPC2")
g = server.grinedit
g.initGraph()
for line in text.split("\n"):
    g.addVertex("BoxVertex", {"label": line})
grinedit.pyは標準入力に流し込まれた文字列を改行で区切ってGRINEditにXML-RPCでクエリを投げるだけ。

合計でも20行いらなかったですね。 後は個人的にはMeadowで箇条書きを書くとGRINEdit上にマインドマップ状のものが生成されるようにしたいところです。まぁ今回試したこれが動くのなら、技術的には難しくなさそうです。

古い記事タイトル一覧

凡例{ ●: 単一エントリーへのリンク, □: そこから最新記事までを一覧表示, ■: そこから最新記事までをwindow.openで開く}(comming soon)

2006年11月28日
 ■ □ By 西尾泰和 at 2006-11-28 16:16:05
2006年11月15日
 ■ □ By 西尾泰和 at 2006-11-15 16:26:30
2006年09月24日
 ■ □ By 西尾泰和 at 2006-09-24 15:53:45
2006年09月13日
 ■ □ By 西尾泰和 at 2006-09-13 16:46:37
2006年09月09日
 ■ □ By 西尾泰和 at 2006-09-09 23:24:21
 ■ □ By 西尾泰和 at 2006-09-09 23:04:19
2006年07月15日
 ■ □ By 西尾泰和 at 2006-07-15 13:54:00
2006年07月04日
 ■ □ By 西尾泰和 at 2006-07-04 13:08:54
2006年06月13日
 ■ □ By 西尾泰和 at 2006-06-13 20:15:47
 ■ □ By 西尾泰和 at 2006-06-13 14:15:45
2006年06月11日
 ■ □ By 西尾泰和 at 2006-06-11 00:51:36
2006年06月02日
 ■ □ By 西尾泰和 at 2006-06-02 17:03:46
 ■ □ By 西尾泰和 at 2006-06-02 12:52:45