Mercurial備忘録

最近非常に興味のあるバージョン管理システム。現在Cygwinで動作中。commit logのエンコードは、環境変数HGENCODINGにutf-8として設定。

Webフロントエンドの方は、強引にレポジトリ内のファイルのエンコードにあわせるよう以下のように対処:

$ env HGENCODING=euc-jp hg serve

hgrcにencodingを設定すればいいらしいのだが、commit logが文字化けした。(0.9.4):

[web]
encoding = EUC-JP

最終的にはwebdir形式で、XAMPPのapacheからCygwinPythonをよびだすように設定。

まだ知らないところとしては、ここら辺。

  • MQでpatch管理
  • graphviz拡張でツリーを概観
  • hgsvnでsvnと連携?

分散型バージョン管理システムは、いろいろあってなかなか楽しそう。

個人的な興味としては、『分散』にはそれほどなく、どちらかというと『苦痛にならないブランチやタグ付け』の方にある。

CVSはかなり苦痛だったし、Subversionも改善はされたがやはり辛いものがある。GNU Archの作者Tom Lord氏のインタビュー記事*1を読んで妙に納得してしまった。

*1:http://www.momonga-linux.org/~tamo/arch_interview.ja.txt

PIC -- A Graphics Language

コンピュータを使って文書の中に図を入れたくなった時、みなさんはどうするでしょうか。

Windowsを使っている人は、Word, Excel, PowerPointで文書を書いていると思います。素直な人だとオートシェイプ機能を使うでしょう。

UnixライクなOSを使っている人は、画像作成ツールを使って画像を作り、それからTeXやDocBook文書、その他の形式の文書に画像へのリンクを書き込むでしょう。あるいは自分独自の文書形式を作って作業しているかも知れません。(そんな人の場合、きっとこの文章は必要ないでしょう。)

TeXがなかった時代はどうしていたのでしょう。roffシステムを使ってパイプ&フィルターで文書を作成していました。

roffシステムは、UNIXのポリシーに従って複数のプログラムの組合せで動作するようになっています。

その中の1つにpicがあります。picはPIC言語を読み込み、roffで解釈できるコマンドに変換するものです。

PICの文法や使い方を学ぶには、Eric S. Raymondの文書を見るのがよいでしょう。はっきりいって、PICの情報源は少ないです。私の知っている情報源について、ここに載せておきます。

  1. man pic
  2. UNIXのバックグラウンド
  3. Brian W. Kernighanによるユーザマニュアル
  4. Eric S. Raymondによるユーザマニュアル
  5. W. Richard StevensによるPICマクロ集

1のmanは、あまり詳しく書かれていません。2はUNIXの思想についてroffを使って解説しています。3,4,5はplotutilsに同封されているマニュアルで、どれもわかりやすくすばらしい文書です。特にEric S. Raymondの文書が参考になるでしょう。

PICをサポートしているソフトウェアも少ないです。私の知る限りでは以下の3つです。

  1. gpic
  2. plotutilsのpic2plot
  3. UMLGraph

gpicは、UnixライクなOSだとおそらく標準で入っているでしょう。pic2plotは、フォントのサポートが不十分です。特に日本語は期待できません。UMLGraphは、単体ではPICを解析できませんが、シーケンスチャートを描くためのPICマクロが用意されています。

Graphvizでハレルチャートが描けるか?

dot言語でがんばってみましたが、限界を感じつつあります。leftyに手を出すか迷ってます。

  • 状態遷移のネストは、subgraphのネストで実現できる。
  • 並列表現を実現するのは、ほぼ無理。
  • subgraphを越えてrank付けするのが難しい。
  • rank付けで見栄えを整えるのが難しい。

状態遷移表データからdotファイルを自動生成するツールを作ってみました。

上の画像は、こんな感じで生成しました:

$ ./stategen.pl displays < harel.txt | dot -Tpng > harel.png

dotファイル自動生成スクリプト

#!/usr/bin/env perl

use strict;

{
    my $name = $ARGV[0];
    graph_gen(state_load(), $name);
    exit(0);
}

sub graph_gen {
    my ($t, $t_name) = @_;

    print "digraph $t_name {\n";
    print "\tlabel = \"$t_name\"\n";
    print "\tlabelloc = t\n";
    print "\tcompound = true;\n";
    while (my ($name, $state) = each %{$t->{$t_name}->{'-states'}}) {
        if (is_nest($t, $t_name, $name)) {
            $state->{'-isnest'} = 1;
            subgraph_gen(1, $t, $t_name, $name);
        } else {
            printf("\t\"%s\" [shape = \"%s\", label = \"%s\"]\n",
                   $t_name . '_' . $name,
                   shape_name($name, $state),
                   $name);
        }
    }
    while (my ($name, $from) = each %{$t->{$t_name}->{'-states'}}) {
        my $from_name;
        my $to_name;

        $from_name = $t_name . '_' . $name;
        foreach my $next (@{$from->{'-next_list'}}) {
            $to_name = $t_name . '_' . $next->{'-name'};
            printf("\t\"%s\" -> \"%s\" [label = \"%s\"]\n",
                   $from_name,
                   $to_name,
                   $next->{'-cond'});
        }
    }
    print "}\n";
}

sub subgraph_gen {
    my ($nest, $t, $super, $t_name) = @_;

    print "\t" x $nest;
    print "subgraph cluster_", $super , '_' , $t_name, " {\n";
    print "\t" x $nest;
    print "\tlabel = \"", $t_name, "\"\n";
    print "\t\"", $super , '_', $t_name, "\" [shape = point]\n";
    while (my ($name, $state) = each %{$t->{$t_name}->{'-states'}}) {
        if (is_nest($t, $t_name, $name)) {
            $state->{'-isnest'} = 1;
            subgraph_gen($nest + 1, $t, $t_name, $name);
        } else {
            print "\t" x $nest;
            printf("\t\"%s\" [shape = \"%s\", label = \"%s\"]\n",
                   $t_name . '_' . $name,
                   shape_name($name, $state),
                   $name);
        }
    }
    while (my ($name, $from) = each %{$t->{$t_name}->{'-states'}}) {
        my $from_name;
        my $to_name;

        $from_name = $t_name . '_' . $name;
        foreach my $next (@{$from->{'-next_list'}}) {
            $to_name = $t_name . '_' . $next->{'-name'};
            print "\t" x $nest;
            printf("\t\"%s\" -> \"%s\" [label = \"%s\"]\n",
                   $from_name,
                   $to_name,
                   $next->{'-cond'});
        }
    }
    print "\t" x $nest;
    print "}\n";
}

sub is_nest {
    my ($t, $super, $name) = @_;

    foreach my $t_name (keys %{$t}) {
        if ($super eq $t_name) {
            next;
        }
        if ($name eq $t_name) {
            return 1;
        }
    }
    return 0;
}

sub shape_name {
    my ($name, $state) = @_;

    if ($state->{'-isfinal'}) {
        return "doublecircle";
    }
    if ($state->{'-isstart'}) {
        return "point";
    }
    if ($name eq 'H') {
        return "circle";
    }
    return "box";
}

sub state_load {
    my $t = {};
    my $tbl;

    while (<STDIN>) {
        s/\x0d\x0a$//;
        s/\x0d$//;
        s/\x0a$//;
        unless ($_) {
            next;
        }
        if (my ($t_name) = /^T:\s*(.*?)\s*$/) {
            $tbl = $t->{$t_name} = {
                '-states' => {},
                '-conds' => []
            };
        } elsif (my ($str) = /^S:\s*(.*?)\s*$/) {
            foreach my $state_str (map {s/^\s+//;s/\s+$//;$_} split /,/, $str) {
                $tbl->{'-states'}->{$state_str} = {
                    '-next_list' => [],
                    '-isfinal' => 0,
                    '-isstart' => 0,
                    '-isnest' => 0
                };
            }
        } elsif (my ($str) = /^s0:\s*(.*?)\s*$/) {
            $str =~ s/^\s+//;
            $str =~ s/\s+$//;
            $tbl->{'-states'}->{$str}->{'-isstart'} = 1;
        } elsif (my ($str) = /^C:\s*(.*?)\s*$/) {
            @{$tbl->{'-conds'}} = map {s/^\s+//;s/\s+$//;$_} split /,/, $str;
        } elsif (my ($str) = /^F:\s*(.*?)\s*$/) {
            foreach my $final_str (map {s/^\s+//;s/\s+$//;$_} split /,/, $str) {
                $tbl->{'-states'}->{$final_str}->{'-isfinal'} = 1;
            }
        } elsif (/^d:$/) {

        } else {
            my ($state, $next, $cond) = map {s/^\s+//;s/\s+$//;$_} split /,/, $_;
            my $next = {'-name' => $next, '-cond' => $cond};
            push @{$tbl->{'-states'}->{$state}->{'-next_list'}}, $next;
        }
    }
    return $t;
}

状態遷移表のフォーマット

T: テーブル名
S: 状態名のリスト
s0: 開始状態
C: 遷移条件名のリスト
F: 終了状態のリスト
d:
遷移元状態名, 遷移先状態名, 遷移条件名
  :

状態遷移表データ

T: displays
S: start, H, time, wait, update, date, stopwatch, chime, alarm2, alarm1, update2, update1
s0: start
C: a, b, c, ^c, d, 2sec in wait, 2min in date,
F:
d:
start, H,
H, time,
time, wait, c
time, date, d
time, alarm1, a
wait, time, ^c
wait, update, 2sec in wait
update, time, b
date, time, d
date, time, 2min in date
stopwatch, time, a
chime, stopwatch, a
alarm2, chime, a
alarm2, update2, c
alarm1, alarm2, a
alarm1, update1, c
update2, alarm2, b
update1, alarm1, b

T: chime
S: start, H, off, on
s0: start
C: d,
F:
d:
start, H,
H, off,
off, on, d
on, off, d

T: alarm2
S: start, H, off, on
s0: start
C: d,
F:
d:
start, H,
H, off,
off, on, d
on, off, d

T: alarm1
S: start, H, off, on
s0: start
C: d,
F:
d:
start, H,
H, off,
off, on, d
on, off, d

参考文献

  1. D. Harel, "Statecharts: A Visual Formalism for Complex Systems", Sci. Comput. Programming 8 (1987), 231-274.
  2. Publications by Prof. David Harel

FreeMindを使ってみました

面白いですねこのツール。結構気に入りました。

ノードの編集モードで下のようにhtmlのタグが入ることを知りました:

<html><a href="http://localhost/">PHPとMySQLで作成する</a>
プロジェクト管理ツール

面白い。htmlでエクスポートすると、ちゃんとリンクされました。

コードの行数から何が読み取れるのか?

先日、ソフトウェアのコンサルタントから、変更のあったコードの行数を数えて下さいという話があった。目的はソフトウェア規模見積もりと進捗管理のためである。

方法はというと、1つのExcelを共有サーバの決められたところに置いておく。このExcelシートのフォーマットはこうである。

  • 1つのレコードは、ファイル単位。
  • カラムは日付毎に区切られ、その中でソースコードの予定行数と実績行数を記述する。
  • 記入された予定行数と実績行数を元にグラフが生成される。
  • あまりにも巨大なため、ズーム機能で60%にしてある。
  • コードの行数を数えるには、Windowsでしか実行できないソフトを使用。

そして、今のプロジェクトの状況はこうである。

  • このプロジェクトのソースコードのファイル数は 6000 を超える。
  • 開発担当者の数は 40 人を超える。
  • ソースコードの編集や、バージョン管理のためのcommit作業は、UnixライクなOS上で行っており、Windows上にソースコードを置いている人は5人もいない。

さて、この話どう思いますか?

ところで、話は変わりますが、今この本を再び読んでいます。

ワインバーグのシステム洞察法  ソフトウェア文化を創る〈2〉

ワインバーグのシステム洞察法 ソフトウェア文化を創る〈2〉

この中で、PPPP(Public Project Progress Poster)というものが出てくるんですが、非常に興味深いです。PHPSVGを利用して、実現できないものかと、いろいろ実験中です。

初めてのPHPプログラミング

Webでデータベースを構築する目的で、初めてPHPを触ることになったのですが、その時手元にあった本がこれ。

オープンソース徹底活用 MySQL4/PHP5によるWebデータベース構築

オープンソース徹底活用 MySQL4/PHP5によるWebデータベース構築

まだPHPの部分しか読んでませんが、解説の仕方や日本語が変なところがありました。CやPerlを既に知っている人が見れば、皆同じ思いになるかも知れません。

  • 「型キャスト」を「配役演算子」と書いてある。
  • ビット演算のXORについての説明が「$aと$bのどちらかだけに一致する部分」
  • ビット演算の反転についての説明が「難しいので計算方法を補足」

「本の説明では、0, '', '0'以外はTRUEって書いてあったけど、実際に動かしてみるとNULLってFALSEと判定された。これって実装依存とかじゃないよね?」とか、「本当にこの言い方であってるのか心配だなあ。」とか、いろいろ疑問が出てきたので、オンラインリファレンスを見ることに。

さんざん否定的なことを書きましたが、悪くないですよ。この本。十分勉強になりました。

PHPで「いいなあこれ」と思った点を上げてみます。

  • ファイルのアップロードとか簡単に書ける。
  • セッションの管理も既に用意されていて、簡単に制御できる。
  • POST, GETリクエストのクエリーを簡単に取り出せる。決まった名前の配列に入っている。
  • CSVファイルからのデータ取り出しも簡単にできる。fgetcsv()という関数が用意されている。

PHPで「お、見慣れない動きだな、これ」と思ったのは、リファレンス。Perlで以下のように書くと「関東」と表示されます。

$a = '関東';
$b = \$a;
$b = '関西';
print($a);

一方、PHPの場合だと「関西」と表示されます。

$a = '関東';
$b = &$a;
$b = '関西';
print($a);

PHPでは、デリファレンスのために明示的になにかする必要がないところがポイントです。

Perlに慣れた人だと、これは結構ハマるところかも知れません。

phpCollabをWindowsマシンにインストール

してみました。経緯としては、いい加減共有フォルダとExcelでの管理にうんざりしてきたからです。

で、感想ですが、プロジェクトの中でグループ分けみたいなことができるといいのになあと。1つのプロジェクトに50人もの人数をぶら下げてフラットに管理するのはちょっとどうかと。

ガントチャートも、特定のグループやある一定の期間のものに絞れたり、そういった機能が欲しいところです。

インストール手順についてですが、以下のサイトに書いてある記事を参考にしました。
http://plaza.rakuten.co.jp/starligit/diary/200508230000/
http://dir.biglobe.ne.jp/col/computer/macos/closeup/CU20060515A/index.htm