yamicha.com's Blog - Presented by yamicha.com
Blog yamicha.com's Blog - 2017/09 の記事
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]

yamicha.com's Blog
 諸事情により、現在更新休止中。ご了承ください。もし今後ブログを再開することがあるとすれば、その際にはこのブログスクリプトではなく、新しく開発したものによるかもしれません。
 当ブログ管理者についてはこちらをご参照。
開発魔法(737)
社会問題(733)
お知らせ(11)
質問・バトン回答(15)
ゲスト出演(8)
経済・知的財産(150)
ゲーム開発(182)
[Ada] 勝手に補足
- Note
- 金配りの次の一手


- Endless Ultimate Diary
- 銃世界

漢字バトン
- うるる雑記帳
- 漢字接力棒

ツキアイゲノムバトン
- ブログ@うにうに画像倉庫
- あぶ内閣

縺イ縺セ縺、縺カ縺励ヰ繝医Φ
- 月夜のボヤキ
- 騎士サーラバトン
パスワードを使う
名無し (2012/02/27)


開発者解放裁判
yamicha.com (2010/03/14)
Winnyに関しては、私も「純白」とまでは考えておりませんし、使用し..

開発者解放裁判
通りすがり (2010/03/08)
winnyに関しては「ダウンロードソフト板」なんてところを拠点に開発..

新型インフルエンザの恐怖
いげ太 (2009/11/03)
> C#などの「int Some(int , int)」は、F#では「(int * int) ->..

時効に関する思考
yamicha.com (2009/08/31)
>いげ太さんコメントありがとうございます。手元にドキュメントが少..
Homepage
Blog Top
Normal View
List View
Search
Calendar
Comment List
Trackback List
Blog RSS
Send Trackback
Phone Mode
Administrator
yamicha.com
Blog
るううるる。
Source
法令データ提供システム
FindLaw
Development
Java2 Platform SE 6
Java EE 6 API
MySQL Developer Zone
PHP Reference
MSDN Library
Ada Reference Manual
Objective Caml
Python Documentation
Erlang
Prolog Documents
カテゴリ表示
 カテゴリ ゲーム開発 に当てはまるもののみを表示します。

 存在する記事 182 件の中から 1-5 件を表示しています。
日本赤十字軍
2006/05/11(Thu)06:12:00
 何やら物騒な「引きこもり支援施設」と称する施設の死亡事件。事の起こりは、被害者の母親が施設に電話をしたということになっていますが、施設側は下見の際に「カギ部屋」と呼ばれる監禁部屋に案内しなかったとか。まあ、するわけがありませんが。
 加害者は逮捕され、今後は罰則が下されることでしょう。それから、「被害者の家族にも落ち度があった」と指摘するのは簡単ですが、おそらく打ちひしがれているであろう遺族にそういう言葉を投げかけるのは、救う会、万引き少年を警察に渡した店主、またはイラクで殺害された香田さんサイドを批判するなどと同等の行為ですから、こちらは据え置きます。
 しかし、何よりの問題はこの施設がテレビで紹介されて連絡先が表示されたという点に他なりません。テレビというのは公共性の高いものであり、ゆえに放送には免許が必要ですし、外国人は放送会社の議決権を20%以上持てないようになっています。また、オウム冤罪事件のように、テレビなどが無罪の人間を犯罪者に仕立て上げることもあります。
 それだけ影響力のあるメディアが、監禁や虐待など何でもありで、結局は命が失われる結末となった施設を紹介したというのはとんでもない話です。犯罪に手を貸しているも同じではありませんか。読売新聞はこの前、佐々氏や海陽学園を美化して取り上げていたりしましたが、このテレビでもトンデモなものが美化して取り上げられていたのでしょう。
 公共メディアで情報を流すからには検証が欠かせません。そして、この施設もしっかり検証さえすれば実態が明るみに出た可能性があります。こんなのは退所者などに話を聞いていけば分かることですし、通常は取材過程で「おや」という点があるはずです。事実、入所者や職員の間では「カギ部屋」なる通称が定着していたといいます。
 テレビ番組がきっかけとなり、その施設で命が失われました。これ、下手すると犯罪なのでは。何より悔しいのは、メディア・スクラムの場合もそうですが、これを問題にする法律がないことです。倫理的には限りなくクロに近いはずの人間が、ぬけぬけとしていられるのですから。
 実際、私としてはいい加減に法整備を行うべきと考えるのですが、敵もさるものです。「報道の自由の侵害だ」とだけ称すれば、政府が手出しできなくなることを十分承知しています。病原体を根絶するのには、患者の治療(今回の場合は施設の摘発)と周囲への感染防止(同施設を報道しない、あるいは実態の報道)を同時に行わねばならないのと同じというのに。
 あと、これは結果論ですから無理な話とは分かっていますが、公共メディアで施設が公にされたからには、関係機関が動けなかったものでしょうか。最近は殺人事件が頻発し、感覚が失われかねませんが、殺人や致死事件は本来これほど重いことなのでは。

 その他、面白い記事が載っていました。若者に献血について聞いてみたところ、「興味はある」と答えたのが5割にもかかわらず、嫌な理由として「痛そう」「怖い」が存在し、しかも提案として「麻酔で痛みを和らげては」などがあるとか。
 それは確かに痛いでしょう。痛いですとも。ただし、開発者はいつでも「痛い」人間ですから(何のことやら)、割と大したことありません。とりあえず、針は派手に太いのですが、見た目ほど痛くはありません。こんなのに麻酔をかけたら余計痛いでしょう。麻酔薬が血に混ざってしまうのもまずいでしょうし。
 で、初めて訪れたらそれは怖いですとも。これはもう、実際にアタックして体験するしかありません。人によっては「2度とイヤだ」という人もいるでしょうが、案外平気なものです。1度体験してみれば、次からはそう怖がる必要はありません。
 ただ、緊張耐性が極めて高いはずの私も、今でもよく脈が高くなってしまうのですが。今年の成人の日の前日に行って、心拍数で撃沈し(自宅で測定しても120/secでした。体調が悪かったようです。翌日には回復)、それっきりです。たまには行かねば、と言いつつも、今の体調ではどうでしょう。ブログ書くのもままならないほど弱っているというのに。そのうち体調を回復してまた行きましょう。
 それでは私からも(半ばおふざけですが)提案を。献血の問診役には男性も女性もいますが、私が見たところ採血を行うなどするのは全員女性です(ただし、若い人ばかりではなくベテランもいます)。ということで、ここはハンサムな男性を仕入れて指名できるようにすれば。いや、これでは指名ホストではありませんか。

 先に扱ったソートアルゴリズムですが、いくらかチューニングしてみました。また、ヒープソートに関しては、やはり私が失敗していたようです。参考文献が分かりづらかったのと、私の理解が浅かったようで。それでは新しく作ったアルゴリズムを。一連のアルゴリズムでは開始点と終了点を設定できますから、実装は少々複雑になっています。
 なお、以前に作ったものに関してはこちらをご参照。
// 修正版ヒープソート
public void heapSort(int sort[] , int start , int end){
	int first = start;
	int last = end;

	// ヒープ構造を作成
	for(last = first; last <= end; last++){
		int r = last;
		while(r != first){
			int p = (r - first + 1) / 2 - 1 + first;
			if(sort[r] > sort[p]){
				int t = sort[p];
				sort[p] = sort[r];
				sort[r] = t;
				r = p;
			}else
				break;
		}
	}
	last --;

	while(first != last){
		// 根ヒープを除外
		int t = sort[last];
		sort[last] = sort[first];
		sort[first] = t;

		last --;

		// 下位ツリーへソート
		int r = 0;
		int top = 0;
		while((top = (r + 1) * 2 - 1 + first) <= last){
			int my = r + first;
			int f = 0;
			if(top + 1 <= last && sort[top] < sort[top + 1])
				f = 1;
			if(sort[my] > sort[top + f])
				break;
			int d = sort[top + f];
			sort[top + f] = sort[my];
			sort[my] = d;

			r = top + f - first;
		}
	}
}
// メモリ確保のオーバーヘッド回避型マージソート
public void refMergeSort(int sort[] , int start , int end){
	refMergeSort(sort , new int[end - start + 1] , start , end);
}
public void refMergeSort(int sort[] , int s[] , int start , int end){
	if(start >= end)
		return;
	int len = end - start + 1;

	// 分解
	int sv[] = new int[2];
	sv[0] = len / 2;
	sv[1] = len - sv[0];

	// 再帰的ソート
	refMergeSort(sort , s , start , start + sv[0] - 1);
	refMergeSort(sort , s , start + sv[0] , end);

	for(int i = start; i <= end; i++)
		s[i - start] = sort[i];

	// 並び替え
	int pos[] = new int[2];
	pos[0] = 0;
	pos[1] = sv[0];

	for(int i = start; i <= end; i++){
		int copy = 0;
		// インデックスの範囲内かチェック
		if(pos[0] >= sv[0]){
			copy = 1;
		}else if(pos[1] >= len){
			copy = 0;
		}else{	// 範囲内なら
			if(s[pos[0]] < s[pos[1]])
				copy = 0;
			else
				copy = 1;
		}
		sort[i] = s[pos[copy]];
		pos[copy] ++;
	}
}
// メモリ確保のオーバーヘッド回避型基数ソート
public void refRadixSort(int sort[] , int start , int end){
	int bv = 4;
	int max = 8;

	int bit = 1;	// ビット深度
	for(int i = 0; i < bv; i++)	// 累乗(bit = 2^bv)
		bit *= 2;

	// 必要なメモリをここで全部確保し、後はそれを使いまわし
	int s[] = new int[end - start + 1];
	for(int b = 0; b < max; b++){
		// バケット必要数を計算
		int use[] = new int[bit];
		// 個数を出す
		for(int i = start; i <= end; i++){
			int d = (sort[i] & ((bit - 1) << (bv * b))) >> (bv * b);
			if(d < use.length - 1)
				use[d + 1] ++;
		}
		for(int i = 1; i < use.length; i++)
			use[i] += use[i - 1];

		// バケット分別
		for(int i = start; i <= end; i++){
			int d = (sort[i] & ((bit - 1) << (bv * b))) >> (bv * b);
			s[use[d]] = sort[i];
			use[d] ++;
		}
		int index = 0;
		for(int i = start; i <= end; i++){
			sort[i] = s[index];
			index ++;
		}
	}
}
 これを実験した結果は以下の通りになりました。実行結果は一例ですが、何度繰り返しても大体この通りになります。今回は200,000個の配列をソートしました。
アルゴリズム時間(ms)
クイック1161
基数(前・通常)4257
基数(前・メモリ節約版)621
基数(新・メモリ節約版)551
ヒープ(新)781
マージ(前)2984
マージ(新)1172
 この通り。何とクイックソートがとんでもなく落ちているではありませんか。前回に作った、メモリを節約しない基数ソートとなると、もう話になりません。配列長のメモリを4ビット(4^2 = 16)分も確保するのですから。ここまでになると、カウントしてメモリを確保した方がオーバーヘッドを避けられます。
 しかし、これでもメモリを毎回確保するオーバーヘッドはバカになりませんから、そこで作成したのが今回のソートです。これは最初にデータ長のメモリ(バケットソートでは必ずデータ長分のメモリが必要)を確保しておいて、それを最後まで使いまわします。そうすると、200,000個の配列を8度作っていたのが1度でよくなり、性能が向上します。
 新しいヒープソートはさすがに速いです。基数ソートに少々劣りますが、基数ソートは制限が多い(例えばユーザー定義のソートに使うことは非常に困難)であることを考えれば、データ量が多いなら実質ヒープソートが最善の選択でしょう。
 そしてマージソートが意外に健闘しています。クイックソートとほぼ同等とは。バブルソートは省きましたが、さすがにこれでバブルソートを動かす勇気はありませんでした。相当な時間がかかる可能性がありますから。

 ついでに作ってみた素早さ実験アプレットがこちら。ただし、これはFFのようなATBではありません。あくまで擬似的にメーターを出しているのであって、すでに全員の順番は決定しています(順番は「ACT」をチェックしてください)。ただし、(このアプレットには素早さの変更処理などは実装されていませんが)途中でキャラのAGIが増減しても大丈夫なように設計してあります。
 これの目新しいところは、何といってもどれほど素早さが高くても、差があっても大丈夫な点に他なりません。相対的に評価しているため、たとえ素早さが1億であっても全くオーバーヘッドなしに動作します。そういうシステムですから。ただ、システムとして洗練されていない(というより実装に迷っている)部分はあります。
 しかし、魔道士イリアスが素早いのは当然として、問題はシェリーさん。騎士サーラが40回行動する間にようやく1回行動できる速度です。

 SQL.36。一応言っておきますが、ネタのストック帳にネタをためたりはしていません。ストックできるほどネタが出たらどれだけ助かるやら。即席でひねり出しては話の構成を作っているので、時としてクエリが破綻したり。

 本日も難題を抱えた魔道士イリアス。側近のサーラに相談します。

騎士サーラ「・・・とすれば、この計算の結果、正味資産は・・・」
魔道士イリアス「サーラ、ちょっと相談・・・って、何かまた難しいことを考え中らしいわね。じゃあ、後にするわ」
騎士サーラ「いえ、構いません。何かありましたら、寝ている時でもご遠慮なくどうぞ」
魔道士イリアス「そう?悪いわね。それで本題だけど・・・」
剣聖ハードゥン「うむ。あの会食を提案してきた国だが、汚職が発覚してボロボロらしい」
騎士サーラ「これまた痛いタイミングですね・・・」
魔道士イリアス「そうなのよ。もう国民が大怒りで一触即発。でも、この政府をどうするかは国民が決めることだけど、ここで私たちが相手国と関係を築いておけば、もし政権がひっくり返ってもある程度は有効だから、今のところ中断の選択肢はないわ」
騎士サーラ「しかし、なおさら治安が悪いのではありませんか?」
魔道士イリアス「だから困ってるのよ。全く、どうしてこのタイミングで、オショクジケンでシュウマイなのよ!」
剣聖ハードゥン「イリアス君、それシャレか?」
騎士サーラ「・・・・・・」
魔道士イリアス「・・・とにかく、話の続きだけど、これじゃなおさらシェリーをガッチリ警護しなきゃいけないわ。その上、非公式に会談の要請まで来ててね」
騎士サーラ「非公式に?」
魔道士イリアス「何か表ざたになると困る話でもするんじゃない?しかも条件付きで、出席者は双方ともに護衛込みで3名だって」
騎士サーラ「・・・3名とは・・・。厳しいですね・・・」
魔道士イリアス「そうなのよ。そこで、以下の条件からメンバーを絞り込んで欲しいのね」
1.メンバーは3名
2.魔道士イリアスには護衛が1名以上必要
3.イリスのシェリーには護衛が2名以上必要
4.これは会談であるため、実務者が最低1人は必要
5.補佐役も最低1人必要。ただし、これは実務者とは別人であること
6.護衛と実務または補佐を兼任することは構わない
7.これは組み合わせの問題であり、順番は関係ない。したがって
例:「イリアス,サーラ」「サーラ,イリアス」この2つは同一である

・メンバー(カッコ内は行える能力)
イリアス(補佐)
サーラ(実務,補佐,護衛)
ハードゥン(実務,護衛)
シェイン(護衛)
シェリー(実務)
アマンダ(補佐)
騎士サーラ「これの組み合わせの数量を求めるのですね?」
魔道士イリアス「そういうこと。やってみて」

 さあ、適切なメンバー構成は見つかるのでありましょうか。

模範解答
 見ての通り、今回のは結構複雑です。実務と補佐が兼任できるようなら、普通に足し算で何とかなるのですが、さすがに補佐役、つまり「アドバイザー」とか「書記」とかを実務者が兼任するなど無理な話です。護衛は単に「もしもの時に戦う」ことが目的ですから、兼任できるのでしょう。
 無論、クエリの実装方法は十人十色、最終的にスマートに答えが出れば構わないのですが、ここでは「実務・補佐がそれぞれ1人ずつは必要」である点に着目します。つまり、実務者枠と補佐枠は事実上「固定」されているのであり、この性質を生かせば比較的容易に検討が行えるでしょう。ではまずテーブルより。
 左から順に、通し番号、名前、こなせる役職(ビット演算。1:実務、2:補佐、4:護衛)、その人に必要となる護衛人数としました。
CREATE TABLE mem(number INT , name VARCHAR(32) , title INT , def INT) ENGINE=HEAP;
INSERT INTO mem VALUES(1 << 0 , 'ilias' , 2 , 1) , 
(1 << 1 , 'sara' , 1 | 2 | 4 , 0) , (1 << 2 , 'harden' , 1 | 4 , 0) , 
(1 << 3 , 'shein' , 4 , 0) , (1 << 4 , 'shelley' , 1 , 2) , 
(1 << 5 , 'amanda' , 2 , 0);
 さて、本題はここからです。実務と補佐は兼任できないのですから、実務者を1つ目のエイリアス、補佐役を2つ目に固定してしまいます。つまり、3つ目には護衛など誰を置いても良いことになります。また、これで自動的に、実務または補佐のない組み合わせは排除されます。
SELECT COUNT(*) FROM 
(SELECT b.name AS 'business' , a.name AS 'assistant' , o.name AS 'other' 
FROM mem b , mem a , mem o 
WHERE b.title & 1 AND a.title & 2 AND 
BIT_COUNT(b.number | a.number | o.number) = 3 
GROUP BY b.number + a.number + o.number) m;

// Result
18
 実務者と補佐役を固定し、重複を排除するだけで18項目まで絞ることができました。しかし、これではシェリーさんが危険ですから、護衛を設定することにします。
SELECT b.name AS 'b' , a.name AS 'a' , o.name AS 'o' 
FROM mem b , mem a , mem o 
WHERE b.title & 1 AND a.title & 2 AND 
(b.title & 4 = 4) + (a.title & 4 = 4) + (o.title & 4 = 4) >= 
GREATEST(b.def , a.def , o.def) AND 
BIT_COUNT(b.number | a.number | o.number) = 3 
GROUP BY b.number + a.number + o.number;

// Result
b	a	o
harden	sara	ilias
sara	ilias	shein
harden	ilias	shein
harden	sara	shein
shelley	sara	harden
shelley	sara	shein
sara	amanda	ilias
harden	amanda	ilias
harden	amanda	sara
sara	amanda	shein
harden	amanda	shein
 すなわち、この11個が組み合わせであることになります。

騎士サーラ「では、メンバーはどうされますか?」
魔道士イリアス「そうねぇ・・・。もし私が入るなら、サーラは欲しいわね」
騎士サーラ「はっ」
魔道士イリアス「サーラがいれば頼もしいけど、サーラだけで護衛から何からするのは大変だから、シェリーも入れたいわね」
騎士サーラ「はい。シェリーさんですね」
魔道士イリアス「でも、いくらサーラが強いからって、もしもの時に護衛が1人じゃ不安だし・・・。老練なハードゥンも入れたいわね。補佐にアマンダも欲しいし。シェインにも警護してもらえるとありがたいわ」
騎士サーラ「・・・イリアスさん、結局全員ではありませんか」
魔道士イリアス「まあ、そうだけど・・・。もうこうなったら占いで決めるわよ。シェリー、タロットはアルカナ?」
イリスのシェリー「イリアスさん、そのシャレ2度目ですよ・・・」
魔道士イリアス「タロットはダメっていうのね。仕方ないわね・・・。こう見えて私はダイスが大好きなのよ。ってことでシェリー、サイを取ってください」
イリスのシェリー「・・・はい、どうぞ。こちらがサイコロジカルラインの資料になっております」
魔道士イリアス「そんなもの渡されたって分からないわよ!」

 このように、SQLは非公式会談にも欠かせない逸品なのです。
カテゴリ [開発魔法][社会問題][ゲーム開発] [トラックバック 0][コメント 0]

阪神大侵災
2006/05/05(Fri)23:36:05
 今度は阪神が結構大変なことになっています。村上ファンドに取締役の変更を申し入れられているのですが、阪神側は断固反対とか。
 私にとって野球などどうでも構わないのですが、村上ファンドが阪神の株を売り抜けるにしても、阪神を支配して資産を売り払うにしても、どちらにせよ極めて腹立たしい行為です。仮に村上側の提案が受け入れられれば、取締役は村上側9人、阪神側7人。村上側のうち1人はやや阪神サイドに近いとはいいますが、さてどうでしょう。
 ともかく恐ろしいのは、阪神は一応電鉄会社です。こういう公共インフラがやすやすと卑怯な成金ファンドに資産目当てに支配され、資産を切り売りされるようでは大変なことですし、仮に経営にまでかかわってきたらなおさら大変です。ああいう他人のことを何とも考えぬファンドが公共機関を支配しようものなら、また福知山のようなことを起こす可能性すらあります。
 最悪のシナリオは、阪神が消滅することでもなければ、村上氏が球団を操ることでもありません。土地を切り売りされることでもありません。おそらく村上氏としては、とっとと株なり土地なりを売り抜ければそれで良いのでしょうから可能性はかなり低いですが、これが電鉄の経営にかかわって大事故を起こすのが最悪のシナリオなのです。
 米国で仮にこういうことをすれば、そうしたファンドは袋叩きの目にあいます。そして、その後はもう誰からも支持してもらえないそうです。日本でもそういう機運はできつつありますし、特に村上氏はTBSを売り抜けたり、阪神に手を出したりと、注目度の高い行為を連発しています。誰もが村上ファンドを嫌うのは当然というものです。
 ところが、米国と違って日本はまだ「最低ファンドは即時消滅」とはいかないようで。「ライブドア被害者の会」の方は「日本は当然のことが認められない国」と称していましたが、要するにそういうことです。詐欺によって株主が損害を受けても、立証その他の責任は株主が持たねばならず、さらに他者を何とも考えぬ最低ファンドは市場から駆逐されないのです。
 しかし、望みがないわけではありません。経済を少しでも理解している人なら、村上ファンドに対して怒りを抱くのは当然ですが、そういう機運が高まれば企業や政治家も村上ファンドと関係が持ちづらくなります。さらに、法改正によるファンド規制も進むでしょう。こうしてジリジリと締め上げていけば、村上ファンドといえども消失することになります。
 それと、取締役の洗い替えですが、村上側に1人「阪神寄り」の人が混ざっており、仮に村上サイドが自己都合で何かを決議しようとしても、この人が反対すると決議は成立しない仕組みです。その本人も「私は村上サイドではない」と自称しているようです。
 では、この提案は受け入れるべきなのか。それがそうとも言い切れないのです。仮に村上ファンドが国民からの批判を和らげるために「暴走防止」を組み入れたのだとしても、不正スレスレの方法による株の買い占めや売りぬけなど、村上ファンドが暴挙を行っているのに変わりはありません。
 さらに、この人が実は村上側の人間であり、阪神を懐柔させて提案を受け入れさせるための「道具」なのだとすれば、提案が採用された直後から村上氏が腕を振るうことがあり得るのです。あらゆる可能性を考えるなら、これを安易に受け入れるのは危険性が高い行為です。何といっても鉄道は公共インフラですから、利用者に迷惑をかける方法だけは採ってはいけません。

 JavaのJDBCにはDate及びTime、Timestamp型があり、それぞれjava.util.Date型を継承しています。つまり、java.util.Dateに定義されたメソッド(getTime()など)を呼ぶことができるわけですが、何でもミリ秒まで扱うことができるとのこと。
 ミリ秒まで使えるとは初耳です。というのも、Servlet Chat企画ではミリ秒まで保存する精度が欲しかったため、時間データをVARCHARで保存するハメになりました(別にBIGINTでも構わないのですが、チャット内部ではデータをStringで扱っているため)。しかし、ミリ秒まで扱えるのであれば、DATETIME型を使わない理由はありません。
 そんなこんなで、まずは使い方の習得からです。DATE型をjava.sql.Dateに読み込むJSPを書いてみました。
Context ct = new InitialContext();
DataSource ds = (DataSource)ct.lookup("java:/comp/env/jdbc/MySQLJSP");

Connection c = ds.getConnection();
Statement s = c.createStatement();
s.executeUpdate("CREATE TEMPORARY TABLE ttime(dt DATETIME)");

c.setAutoCommit(false);

PreparedStatement ps = c.prepareStatement("INSERT INTO ttime VALUES(NOW())");
ps.executeUpdate();

ResultSet rs = s.executeQuery("SELECT * FROM ttime");
while(rs.next()){
	Date d = rs.getDate(1);
	out.println(d.toString() + " " + d.getTime());
}

s.executeUpdate("DROP TABLE ttime");
s.close();
c.close();

// Result
2006-05-05 1146754800000
 1146754800000とは、これまたキリのいい数字だことで。しかも、何度読み込んでもこの数値です。試しに60*60*1000で割った余剰を求めたところ、あまりはゼロでした。どうやら、少なくとも1時間単位または1日単位の精度しかないようです。これでは話になりません。
 そんなこんなで、次はTime型を試してみました。とはいえ、SQLのテーブル型をDATETIME型にしたままではエラーが出るため、こちらもTIME型としました。
s.executeUpdate("CREATE TEMPORARY TABLE ttime(dt TIME)");

c.setAutoCommit(false);

PreparedStatement ps = c.prepareStatement("INSERT INTO ttime VALUES(NOW())");
ps.executeUpdate();

ResultSet rs = s.executeQuery("SELECT * FROM ttime");
while(rs.next()){
	Time t = rs.getTime(1);
	out.println(t.toString() + " " + t.getTime());
}

// Result
22:47:46 49666000
 こちらの精度は秒単位。やはりMySQLでミリ秒は使えないようです。ミリ秒を使いたければ、BIGINTかVARCHAR、DECを使いましょう、ということで。例えばBIGINTならlongで取得してjava.util.Date型にセットすればOKですし、VARCHARならLong.parseLongで。
 しかし、ここで問題が1つ。これではSQL DATETIME型が扱えないのです。JavaのDate型では値の精度が低くなりますし、かといってTime型を使うとエラーが出てしまいます。仕方がないので、私らしい小細工を使ってみました。
s.executeUpdate("CREATE TEMPORARY TABLE ttime(dt DATETIME)");

c.setAutoCommit(false);

PreparedStatement ps = c.prepareStatement("INSERT INTO ttime VALUES(NOW())");
ps.executeUpdate();

ResultSet rs = s.executeQuery("SELECT * FROM ttime");
while(rs.next()){
	Object t = rs.getObject(1);
	out.println(t.getClass().getName() + " " + 
		((java.util.Date)t).getTime());
}

// Result
java.sql.Timestamp 1146837168000
 この通り、正しい精度が得られました(ミリ秒が使えないのは残念ですが)。どうやらDATETIME型はjava.sql.Timestamp型で処理するようです。これには気づきませんでした。というのも、MySQLのTIMESTAMPは非常に限定的なものであり、所要領域は小さくて済むものの、2037年問題を回避できませんので、今から使うようなものではありません。2037年までテーブルを使う気がないとしても、もしSQLのベンダが2037年問題をクリアできるようにTIMESTAMPの動作を拡張すれば、不整合が起きる危険性すらあります。
 その点DATETIMEは9999年まで使うことができますので、2037年問題は回避できます。ですから私はこちらを好んで使うのですが、上記の通りSQLではDATETIMEとTIMESTAMPは厳密に区別されたものですから、Javaでは混乱を招きます。しかし、JavaのTimestamp型はしっかり2037年以後の日付を表せるのでしょうか。
 そんなこんなで'4096/03/17 00:00:00'をセットしてみた結果、JavaのTimestampで正しく扱うことができました。ついでに、java.util.Dateに定義されているgetYear()に1900を足してみたところ、しっかり4096が返ってきました。SQLのDATETIMEで扱えるのは9999年までで、Java Date型で扱える年数は理論上3000億年弱になりますから、当分は年号の不安を抱く必要はなさそうです。

 ついでに「続・素早さ考」。先に「素早さは相対的に評価するものである」と述べましたが、なかなか難しいのです、これが。
 例えば、行動の基準値が100であるとします(素早さを加算していき、これに達すると行動できる)。そして、素早さ20のキャラと10のキャラがいるとします。すると、20のキャラは10のキャラの2倍行動ができることになります。ここまでは良いのです。
 ところが、素早さの基準が100なのに、キャラの素早さが1000や500になったらどうしましょうか。行動後には素早さの加算値がゼロにリセットされるのであれば、両者とも同じだけしか行動できないことになり、素早さシステムが破綻します。かといって、行動するたびに加算値から基準値分(100)を減算するのでは、一見上手くいきそうですが、これもまた破綻します。
 例えば魔道士イリアスの素早さ加算値が1000、騎士サーラが500とします。基準値が100であるなら、両者とも行動できることになります。この場合、加算値のより高い魔道士イリアスの方を先に行動させるとしましょう。すると魔道士イリアスの加算値は100減って900、騎士サーラは変わらず500。また魔道士イリアスが行動できます。そうすると800と500になり、また魔道士イリアスが行動できます。素早さが2倍しか違わないのに5回も連続行動されようものなら、これではゲームバランスもへったくれもないです。
 これを回避する手段としては、基準値を十二分な値にしておくなどの方法がありますが、やり込むようなゲームなら素早さが何十万、何百万まで上昇してもおかしくありません(ちなみにTactical Revolutionでは正規の手段ではレベルは99までしか上がりませんが、intの範囲内なら100だろうが1億だろうが2^31-1の範囲までプログラマーが好きなように設定できます)。ということで、これではループに時間がかかってしまうなど、弊害が目立つようになります。
 となると、やはり素早さは相対評価でなければならないのです。そこで私が実装したのが、最も素早いキャラの素早さを基準値とするプログラムです。基準値に矛盾が生じる理由は、要するに「素早さが基準値を上回るから」なのですが、この方法なら絶対に素早さは基準値を上回りません。
 が、ここで問題になるのが「素早さが変化した場合」。最も素早さの高いキャラの素早さが、ステータス変化によって増減したり、そのキャラが消失したり、またはもっと素早さの高いキャラが現れたり、といったことはあり得る話です。この場合は素早さの基準値も修正しなければなりませんが、ただ単に修正するだけでは問題が出ます。
 例えば素早さが100のキャラと10のキャラがいるとします。この場合、基準値は100です。が、素早さ10のキャラが加算値90まで行ったところで、素早さ100のキャラの素早さが5になってしまいました。この場合、基準値は10に設定し直されますが、そうすると素早さ10のキャラには加算値が90あるわけですから、9回連続行動ができてしまいます。
 かといって調整しなければ、例えば素早さ100のキャラと50のキャラがいたとして、両者が加速してそれぞれ600と200になったとすれば、基準値が100のままなのに素早さが600という状況になってしまい、やはり破綻します。
 というわけで、こうした問題を解決し、素早さの変化があっても対応できるようにするための実装をちまちまと。不毛ではありますが、こういうのを考えるのもなかなか面白いものです。

 SQL.32。最近少しばかり疲れているようです。SQLに限らず。

 どういうわけか本日は皆さん大憤慨。その矛先は・・・。

剣聖ハードゥン「サーラ君、これはどうなっているのだ?」
騎士サーラ「え?何がですか?」
剣聖ハードゥン「実はな・・・。私がまとめておいた我が国と周辺国の軍事力データが、イリアス君のアカウントによって消失させられたのだ。幸いバックアップはあったが、数日分の作業がパーだ。これでは我が国の保安上、重大な結果を招きかねない」
騎士サーラ「えっ、ハードゥンさんもですか?」
剣聖ハードゥン「どういう意味だ?」
騎士サーラ「実は私も、ノートに理力の計算をしていまして、とんでもないことになったのです」
剣聖ハードゥン「とんでもない・・・?」
騎士サーラ「さほど大きな力は生じない計算のはずが、物質750mgの質量エネルギーに相当するジュール値が得られまして・・・。こんな原爆のような力が生じるわけがないと考えて、資料を見直したところ、ノートの数字が1箇所だけ書き換えられていたのです。力の計算では、一部の数値を変更するだけで、大きく内容が変わってしまうことがあります」
剣聖ハードゥン「何かの間違いではないか?」
騎士サーラ「いえ、間違いありません。よく見るとその部分だけ、用いている鉛筆の芯の種類が違いました。それに、あれはイリアスさんの筆跡です」
イリスのシェリー「サーラさん、おられますか?」
騎士サーラ「シェリーさん、血相を変えてどうされたのですか?」
イリスのシェリー「誰ですか、師団の所有する建物の玄関部分が火災で消失したのを”減価償却”として仕訳たのは。その上ご丁寧にも、受取手形の増加を全部貸方に書いて・・・。世にも珍しい、受取手形による債務超過ですよ。おかげで検証に数時間・・・」
騎士サーラ「・・・これは、イリアスさんの字ですね・・・」
イリスのシェリー「しかも、保存しておいた”マクロ経済原論”の文書まで書き換えられて、大問題になるところでした。もしとっさに気づかなかったら、グレーナイトどころかデフレとデノミの区別もつかない人間呼ばわりをされかねませんでした」
弓兵アマンダ「イリアスはどこ?」
騎士サーラ「今度はアマンダさん、どうされたのですか?」
弓兵アマンダ「私の友人の結婚式にイリアスが現れて、”別れの予感”を熱唱して行って、後から私がとりなすのにどんなに苦労したか・・・。衣装がギンギラだったから、顔までは見えなかったけど、あの声は確かに・・・」
聖騎士シェイン「イリアスさんはどちらですか?作業中、何かおかしいと考えて調べてみたところ、統計の母集団が改変されていたのですが・・・。おかげで分散から何からやり直しです」

 とまあ、この通り、なかなか大変な状況になってまいりました。が、タイミングがいいのか悪いのか、そこに現れたのが魔道士イリアス。しかもなぜか傷だらけです。

魔道士イリアス「みんなそろって、一体何事なのよ?」
騎士サーラ「イリアスさん、どうされたのですか、そのケガは・・・」
魔道士イリアス「緊急の用があってね。本当はサーラに声をかけようとしたけど、理力の計算で大変そうだったから、近くにいた師団の仲間と出撃してたのよ。一言くらい伝えておくべきだったわね」
騎士サーラ「・・・とすると、イリアスさんはしばらく、外出しておられたのですね」
魔道士イリアス「ええ。ちゃんとした時間まで覚えてはいないけど、詳しくは師団のシオドアとフランシスが知ってるはずよ。それで、一体何ごとなの?」
騎士サーラ「実は・・・」
魔道士イリアス「ええっ!?私が?あのねぇ、理力の値を的確に書き直したり、帳簿や経済論をいじったり、統計を変えたり、そんな高度なことが私にできるように見える?大体みんな、私がやってるのを直接見たわけじゃないし、第一私は外出してたわよ」
騎士サーラ「とすると・・・師団の誰かが犯人、ということですか?」
魔道士イリアス「そういうことね。部外者が私の筆跡をマネしたりはできないし、この建物で堂々と行動して見つからないはずがないわ」

 それらしい人物を集めたところ、犯人候補はアリス、シャル、ミカエル、リサの4人でした。それぞれの供述は以下の通りです。

師団のアリス「私はやってないわ。ただ、最近シャルとリサがどうにもおかしいから・・・。犯人がいるとすればこの2人ね」
師団のシャル「私は無関係だ。もしこの中に犯人がいるとすれば、アリスとミカエルではないか」
師団のミカエル「全くの事実無根、私は何も存じません。ただ、アリスが犯人とは考えられません。私が考えるに、リサが犯人では・・・」
師団のリサ「えっ、私は知りませんよ。しかし、犯人はミカエル、シャルなのでは・・・」

魔道士イリアス「うーん・・・。私が考えるに犯人は3人以上じゃないわね。手口からして」
騎士サーラ「しかし、普通に考えても話が合わないのでは・・・」
魔道士イリアス「だから、今回の犯人は知能犯ね。犯人でない人は言っていることは全部正しいけど、犯人の場合は時に嘘の場合もあるわ。無論、嘘じゃない場合もあるわ。全く、厄介よね」

 さあ、誰が犯人なのでしょうか。迷宮入りになりそうですが、SQLさえあればそうはいきません。名探偵イリアスに代わって犯人を当ててください。

模範解答
 今回は、犯人は全員「知能犯」ですから、今回のテーブル構造は「1 = まっとう」「0 = 知能犯」で大丈夫でしょう。それにしても、いよいよもって全員知能犯とは。犯罪も進化したものです。最初は3人の中から犯人を暴くだけだったというのに。ついでに、最初から人数条項は盛り込んでおきます。
CREATE TABLE lie(lie BOOLEAN) ENGINE=HEAP;
INSERT INTO lie VALUES(TRUE) , (FALSE);

CREATE TABLE mem ENGINE=HEAP 
SELECT l1.lie AS 'alice' , l2.lie AS 'shall' , l3.lie AS 'michael' , 
l4.lie AS 'lisa' FROM lie l1 , lie l2 , lie l3 , lie l4 
WHERE !l1.lie + !l2.lie + !l3.lie + !l4.lie < 3;
 知能犯ばかりということで、話だけ見ると難しそうですが、人間があれこれ考えなくて良いのがSQLというものです。知能犯というのは要するに「ペテンのプロ」であり、嘘を見抜くには最初からその言葉を信用しない、つまり犯人の言葉は無視して考えるほかありません。相手が師団の不正犯だろうが、堀江だろうが、上祐だろうが。
SELECT * FROM mem WHERE (!alice OR (!shall AND !lisa)) AND 
(!shall OR (!alice AND !michael)) AND (!michael OR (alice AND !lisa)) AND 
(!lisa OR (!michael AND !shall));

// Result
alice	shall	michael	lisa
1	0	1	0
 おやおや、あっという間に出てしまいました。すなわち、犯人はシャルとリサであることが分かります。恐るべしシークェル。
 その後、師団のアリスの「シャルとリサはおかしい」という証言により、ある時点でこの2人が混乱をもくろむ別人と摩り替わっていたことが分かりました。

魔道士イリアス「ふーん、グレーナイトって本当にあるのね」
イリスのシェリー「ありますよ。ホワイトナイトは友好的、ブラックナイトは敵対的、グレーナイトはその中間というわけです」
魔道士イリアス「ホワイトナイトといったらサーラよね。カッコいいし。じゃあ、750mgの質量エネルギーって?」
騎士サーラ「物質375mgと反物質375mgを衝突させた際に生じる膨大なエネルギーです。それらの存在が消失する代わりに、質量として存在していた分のエネルギーを放出します」
魔道士イリアス「良く分からないけど・・・まあいいわ。じゃあ、統計って?」
聖騎士シェイン「母集団に対する集合と分散の値としては、SQLの各関数でも求めることができ、その際は母集団が仮に5、値が4.5、5.2・・・であるなら・・・」
魔道士イリアス「ああ、もういいわ。頭痛いわね」
騎士サーラ「シェインさん、分散から得られる経験的な平均として、256が・・・で・・・それが・・・」
聖騎士シェイン「その場合、経験上の平均値は・・・で・・・といったところですね」
騎士サーラ「・・・とすると、理力の影響力は・・・と、大体この辺りに・・・」
剣聖ハードゥン「シェリー君、例のことだが、調べてくれたか?」
イリスのシェリー「はい。B/Sの結果次第でPBRが・・・だから・・・毒薬条項が・・・筆頭がグレーナイトに・・・」
剣聖ハードゥン「なるほど。しかしそれだと・・・キャッシュフローはどうだ?」
魔道士イリアス「な、な、何よ。どうしてみんな、そう意味不明な言語をしゃべるのよ」
弓兵アマンダ「あ、イリアス。ちょっと聞きたかったんだけど。この場合のクラスモデルとして、このクラスの値をこっちに渡して、これを・・・」
魔道士イリアス「いや、その場合はそれを抽象にして、オーバーライドした方が効率がいいわね。そっちはスタティックにするといいわ。クラスのデータは関係ないから」
師団のアリス「何、このかみ合わない会話・・・。この師団、どうやって今まで続いてたのか、不思議でならないわ・・・」

 このように、SQLは知能犯発見にも極めて強い威力を発揮する逸品なのです。
カテゴリ [開発魔法][社会問題][ゲーム開発] [トラックバック 0][コメント 0]

正義なき新聞と新聞なき正義なら
2006/03/17(Fri)20:33:18
 Javaアプレットといえば、おそらく半数はゲームに使われているでしょうが(無論それなりに実用的なものも作れますが。私もチャットを作りましたし。しかし、ファイル保存ができないなど致命的な弱点が多々あるため、実用性はかなり落ちます)、それに関して。
 Episode.1の時はJavaをほとんど習得しておらず、C/C++から入った私はオブジェクト指向に関しても良く理解していませんでしたので、ただひたすら動くように作りました。当初は何もかもをAppletの継承クラスで実装していたのですが、あまりに肥大化しすぎたためステータスやエフェクト用にクラスを作ったという経緯があります。
 このようにクラスを分離させてはみたものの、相変わらず基底クラスのバカでかさといったらすさまじいのですが、バラしたクラスを使ってみるとこれが結構便利なのです。ですから最後の方の実装は「オブジェクト指向モドキ」になっていますが、大半をオブジェクト指向など無視してコーディングしたため、アプレットの継承クラスにあらゆる機能が集中しています。
 それを踏まえてTypingWindはオブジェクト指向でコーディングしましたが、例えばフィールドと戦闘画面は基底クラスが同じになっています。で、どのように使っているかといいますと、基底クラス型のstatic変数に対して描画やリスナ処理を行うようにしておき、これに戦闘やフィールドのインスタンスを代入することで画面を切り替えているのです、これが。
 少々荒っぽい手段ですが、動きます。しかも実装はかなりお手軽です。というのも、この方法で実装する場合、いかなる場合でもアプレットの継承クラスからの要求はスタティック変数にそのまま丸投げすれば良いわけですから。弱点としては、例えば戦闘に入るとフィールドクラスのデータは全部なくなるため、必要なデータはすべてstaticで管理する必要がありました。
 Tactical Revolutionでも似たような仕組みを使っていますが、staticメソッドではなくクラスを1つかませ、フィールドや戦闘画面のクラスへはそこからアクセスするようにしています。この中間のクラスが画面を媒介するようになっており、例えばステージを1つ選択すると、ステージの番号が中間のクラスに渡され、このクラスが新しいインスタンスを作って実行している、というわけです。
 こうして完全オブジェクト指向として作ったTactical Revolutionではありましたが、フィールドなどのクラスからさらにクラスを呼ぶことはできないという致命的弱点があります。また、例によってデータはしっかり保管しておかなければ消えてしまいます。途中までしか遊べない「New Game」モードの場合、データはstaticで管理しています。
 そこで考えた(というより本日突然ひらめいた)のが、「こうしたクラスから別のクラスを呼べるようにならないか」、さらには「メニューウィンドウもこれで実装してしまえないか」といったことです。実はWindowsもこれと似たような仕組みで動いているわけですが、それなら実装できないわけがありません
 まず前提条件ですが、

1.MouseListener、MouseMotionListener、KeyListener、描画メソッドのすべてがコールバックとして利用できなければならない
2.Threadを継承しなければならない
3.基本処理を書いた1つの抽象基底クラスを継承し、様々な処理が実装できなければならない。また、派生先では目的外のコーディングが最小限で済むようにしなければならない
4.メニューやダイアログとして利用する場合を考え、クラスからクラスを呼び出した場合は、呼び出し元の描画もなされるように、及び呼び出し元にもキーやマウスの動作を渡せるように実装することができなければならない(無論、これがなされない実装もできなければならない)
5.描画及び操作にsynchronizedロックがかけてあり、その上にスレッドのrun()もしくはそこから呼び出されるメソッドにsynchronizedロックがかけてある場合で、そのロック内からクラスが呼び出された上、呼び出し側は呼び出されたクラスのスレッドが終わるまで待機し、さらに呼び出されたクラスの側から呼び出し元のsynchronizedメソッドが呼び出されるような場合でも、デッドロックが起こらないようにしなければならない
6.ここで作るのはいわば「ワークフレーム」であるが、定数以外のstatic変数を極力使用してはならない

 結構難しいです。実は5番が問題で、Tactical Revolutionでもかなり苦労した部分です。例えばTactical Revolutionの場合、
// 実際にはこのような実装及び変数名ではありませんが
// 分かりやすさのために相当簡略化しています

synchronized public void run(){
	message.set("会話メッセージ");
	message.waiting();	// メッセージが閉じられるまで待機
	// ...
}
synchronized public void draw(Graphics g){
	message.draw(g);
	// ...
}
synchronized public void mousePressed(MouseEvent e){
	message.mousePressed();	// メッセージを閉じる
	// ...
}
 さて問題、これを実行するとどうなるでしょうか。まず、メッセージが閉じられるまで待機している時点でロックが取得されていますから、当然drawメソッドは実行されませんから、描画が行われない状態になります。また、mousePressedメソッドも実行されないため、永久にメッセージが閉じることはありません。見事なデッドロックの完成です。
 仕方がないのでTactical Revolutionでは「1マイクロ秒waitさせてはメッセージウィンドウが消されていないかチェック」という実装で(wait中はロックが解除される)監視を行っています。しかし、今回ははなっから「ウェイト状態の解除はinterrupt()」を前提として作成しました。この場合、待機スレッドとは別にinterrupt()を呼び出すスレッドが必要ですから、Tactical Revolutionの方式の方がリソース消費は少なそうですが。
 ちなみにTactical Revolutionの場合、メッセージなどのウィンドウはあくまでクラスのオブジェクトですが、今回の実装では選択ダイアログも1本のスレッドを持ちます。構造としては、Threadを継承して処理のベースクラスを作り、さらにそれを継承してダイアログ用にデッドロックフリーの待機メソッド(このクラスのスレッドが終了するまで呼び出し元のスレッドを待機)などが定義されたクラスを作っています。
 待機メソッドが使いたければダイアログ用クラスを継承すれば良いのですが、そうでないクラスでもデッドロックフリーの待機を実現するため、間にもう1本スレッドをかませて待機を行うためのクラスも実装しました。1つ余分にスレッドは立ってしまいますが、これで(ベースクラスさえ継承していれば)あらゆるクラスに対して待機が実現できます。
// 待機専用クラスの仕組み
public class 待機クラス extends Thread{
	public void run(){
		呼び出し先.join();
		呼び出し元.interrupt();
	}
	public void 待機メソッド(){
		start();
		呼び出し元.wait();
	}
}
 他に1番の条件に関しては、イベントリスナを登録したり描画先のクラスを指定するメソッドを書いたインタフェースを作り、それをアプレットを継承したクラスにインプリメントした上で、コンストラクタでそのインタフェース型を受け渡すようにしました。3番の条件はオブジェクト指向で作っている限り自動的に満たされます。
 後は4番ですが、同じくコンストラクタで呼び出し元のクラスオブジェクトを呼び出し先に渡すようにしました。その上で、描画やイベントメソッド内で呼び出し元のオブジェクトの同じメソッドを呼び出せば、呼び出し元の描画やイベント処理を行うことができます。さらに、この構造は階層構造のようなものですが、重要な機能を含んでいます。
 例えば適当な画面Xから選択ウィンドウAを呼び出したとします。選択ウィンドウAの描画処理では、Xの描画メソッドをコールしています。さて、ここでさらに選択ウィンドウAから選択ウィンドウBを作りました。そして選択ウィンドウBも呼び出し側の描画メソッドをコールするようにしました。すると、BからAの描画メソッドがコールされ、さらにAからXの描画メソッドがコールされるため、Xの画面の上にAのウィンドウ、その上にBのウィンドウが描かれるのです。
 これでフレームワークは完成しました。後はこれを使うアプレットを考えるだけですが、これが決まらないから困っているのではありませんか。

 仏で大変な法律が作られそうになっています。若年者雇用促進とかで、26歳までの社員は雇用後2年までは無条件で解雇できるそうです。つまり、雇用されても2年以内なら「もうあなたは明日から来なくて良い」と言われれば、仮に自分に非がなかろうが、会社都合だろうが何だろうが、理由を聞くことも許されずに解雇されるしかないわけです。
 これはすさまじい失業率を改善するための措置だそうですが、どのような条件を付けたとしてもポストの数はたいして変わらないわけですから、結局雇用が流動化するだけのような気がしますが。早い話が「フリーター社員」のようなものです。このような手段にまですがるとは、日本の状況など仏から見ればうらやましいようで。

 ここで名言を1つ。「インターネットなき新聞と新聞なきインターネットなら、私は迷わず、いや喜んで後者を選ぶ」。というのも、「情報源秘匿問題の判決に対して全マスコミが怪しいまでに声をそろえて抗議」と「新聞と教科書の特殊指定見直し」の2つの問題が重なっており、いつもよりさらにボロが出ているのです。
 まず後者ですが、新聞業界に続いて教科書業界も反対の嵐です。新聞はまだ社会的に反対する理由があるとしても、教科書にはないはずですが。つまり、それが新聞であれ教科書であれ、こうした規制緩和に業界は必ず大反対を行うということであり、裏返せば新聞社も「特殊指定見直しが社会のためにならない」などとは本気では考えておらず、ただ単に自分の利益のためだけに反対していることが分かります。公益のためなどと言い訳せずに、「我々の利益が損なわれるのが嫌だから反対します」と素直に言えば、まだ同情もできるというのに。
 当然、新聞はすぐにでも特殊指定を見直すべきでしょう。新聞などもう長くないメディアですし、その上に「特殊指定があるから山間部にでも配達できる」といった言い訳までしているようですが、特殊指定があってもなくても山間部の配達が不採算なのは変わらないでしょう。さすが新聞屋さん、詭弁が上手でいらっしゃる。
 さらに判決に関してですが、当然これは不当判決でしょう。新聞が情報源を秘匿しようがしまいがどうでも構いませんし、実際のところ「情報提供者の権利を守るために秘匿しよう」のような見上げたことはこれっぽっちも考えていないのでしょうが、こうした判決で善良なる情報提供者の利益が害されることだけは許せません
 この判決は読売新聞の記者の問題ですが、こういう時だけ全マスコミが不気味に歩調をあわせるというのも恐ろしいです。今回の決定は確かに不当判決ですが、これが仮にマスコミに不利な判決ではあってもまっとうな判決だとしたらどうでしょう。ペンは剣より強しとはこういうことを言うのでしょう。無理が通れば道理引っ込むとも。
 実際、こういう傾向は非常に恐ろしいものがあります。読売新聞は大量破壊兵器の存在をねつ造して謝罪もせず、政府にくみして理不尽な論調で民主党批判などを繰り返していましたが、そういう「政府にゴマすり新聞社」ですら、せっかく大好きな政府に有利な判決が出たのに、自己利益が絡んでくるとこれです。野球再編問題の時は「超高額所得者のスト」などとインチキ論理で選手らを批判しましたが、マスコミ自体がこういう体質ですから。
 日本の全国紙で最もジャーナリズムに近い新聞社である朝日新聞などは、そのおかげで一部から理不尽な批判にさらされることもたびたびあります。これが政府を敵に回すリスクです。つまり、報道機関が政府権力を敵に回すというのは、それだけの覚悟が必要なのです。そういう代償を払った新聞社でもなければ、「いいとこ取り」は許されません。
 また、例えば毎日新聞は(完全とはいえないまでも)マスコミ批判が入った遺族のコメント全文なども比較的しっかり記載していてその点は好感が持てるのですが、言うまでもなくこれは自らの評価を落としかねないリスクを引き受けた結果です。そのリスクも取らないような新聞社には、遺族に取材を行ったり、コメントを記載したりする権利はありません。
 そういえば大半の新聞社の皆さん、規制緩和の流れを大絶賛していらっしゃいましたね。その規制緩和の次なる対象として新聞も含まれた。それだけのことではありませんか。他だけ規制緩和させておいて自分の業界だけさせない、というのは許されません。これに反対して良いのは、規制緩和を批判していたマスコミのみです。
 とまあ、こうして見てみるだけでもマスコミが信用ならないと言われるのは当然の結果というものでして。とにかく、今回ばかりはマスコミの自己都合的「ペンは剣より強し」「無理が通れば」論理に負けてはいけません。言い出した以上、特殊指定見直しは必ず行われるべきです。皇室のようなバカげた結末はもう不必要です。

 以前にMySQLのストレージエンジンを扱った際、FEDERATEDテーブルを使うことができませんでしたが、検索してみたところ「FEDERATEDテーブルで自分のデーモンを参照した場合にはテーブルが作成できない」という同様のエラーに悩まされている人も存在する様子。
 これはまさか、自分のデーモンを参照しては使えないのでしょうか。ということは、1つのPCにポートの違う2つのデーモンを用意するか、2つのPCで使うしかなさそうです。前者の方法はPCのスペックからして少々無理がありますので、後者の方法を用いることにしました。接続先は「sub.localyamicha.com」(192.168.0.2)です。
 FEDERATEDテーブルを使用する場合、参照先のSQLにも全く同じテーブル構造のテーブルが存在しなければなりません。つまり、2つのPCで作業するなり、プロンプトを2つ開いてそのうちの1つで相手側のSQLに接続するなりしなければなりません。これを踏まえてFEDERATEDテーブルを作成してみるとしましょう。
 まずは5.0のマニュアルのサンプル通りにMyISAMで。PRIMARYとINDEXの2つを用いたのは、以前に「INDEXが指定されていない」エラーに見舞われたため、エラー回避を考えて念のために入れてみたのですが、結局は不必要でした。
// sub.localyamicha.com(サブPC)
USE test;

CREATE TABLE fed(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number) , INDEX(number)) ENGINE=MyISAM;

// www.localyamicha.com(メイン・サーバーPC)
CREATE TABLE fed(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number) , INDEX(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/fed';

INSERT INTO fed VALUES(0 , 'Sara') , (1 , 'Harden');

// sub.localyamicha.com
SELECT * FROM fed;

// Result
number	name
0	Sara
1	harden
 では、次はInnoDBを使ってみます。
// sub.localyamicha.com
CREATE TABLE fe2(number INT , name VARCHAR(32) , PRIMARY KEY(number));

// www.localyamicha.com
CREATE TABLE fe2(number INT , name VARCHAR(32) , 
PRIMARY KEY(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/fe2';

ERROR 1121 (42000): Column 'number' is used with UNIQUE or INDEX 
but is not defined as NOT NULL
 どうしたものでしょう。変なエラーが出てしまいました。これ、実は「参照先のカラムはNOT NULLなのに、作成されたテーブルのカラムはNOT NULLになっていない」ということらしいです。確かに参照先のテーブルはPRIMARY KEYの暗黙的NOT NULLを利用しましたが、PRIMARY KEYといったら指定しなくてもNOT NULLと考えるのが普通でしょうよ。
// 上のコードより続く
CREATE TABLE fe2(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/fe2';

INSERT INTO fe2 VALUES(0 , 'Ilias');

// sub.localyamicha.com
SELECT * FROM fe2;

// Result
number	name
0	Ilias
 ストレージエンジンなどには関係なく動くようです。ということは、まさかビューも動いてしまう、ということはさすがになさそうですが、ダメもとで
// sub.localyamicha.com
CREATE VIEW feview AS SELECT * FROM fe2;

// www.localyamicha.com
CREATE TABLE feview(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/feview';

INSERT INTO feview VALUES(1 , 'Shelley');

// sub.localyamicha.com
SELECT * FROM fe2;

// Result
number	name
0	Ilias
1	Shelley
 動くのですね、これが。どうやらテーブルとして参照さえできれば何にでも使えるようです。そういえば、ドッキリいたずらとして「デスクトップのスクリーンキャプチャを壁紙に設定し、アイコンは非表示にしてしまう」というものがありましたが、FEDERATEDテーブルに関しては参照先のテーブルをリネームして保管しておき、代わりに同じ構造でBLACKHOLEテーブルを作っておくと、かなりいじわる度の高いいたずらができるのでは。コネクトできないのならともかく、クエリは成功したのになぜかデータが記録されない、という。

 ところで、紙に書かれたリストを見てふと考えたのですが、SQLの集計はどこまでのことができるのでしょうか。例えば「騎士と魔道士の2つのカテゴリが存在する場合で、それぞれの人数を返す」といったクエリは即座に打てますが、それなら「飲み会で騎士からは50ティル、魔道士からは40ティル徴収することにした。ここに参加者リストがあるとして、全部で何ティルの予算が得られるか」といった問題を1つだけのクエリで簡潔に実行するにはどうすれば良いでしょう。これは色々と応用が利きます。
 そういえば、以前にORDER BYに条件式を書いたことがありました。条件式を複数記述し、全部にDESCキーワードを指定しておけば、先頭から順に条件に当てはまるものをソートすることができるのです。これを集計でも応用できないものでしょうか。まず基本形より。
// サーラの練習試合結果テーブル(対戦相手 , 勝敗)
CREATE TABLE combat(enemy VARCHAR(32) , result VARCHAR(32));

INSERT INTO combat VALUES('イリアス' , '敗北') , ('ハードゥン' , '勝利') , 
('シェイン' , '勝利') , ('シェイン' , '勝利') , ('ブライトネス' , '勝利') , 
('サラサーラ' , '引き分け') , ('シェイン' , '勝利') , 
('ハードゥン' , '引き分け') , ('シンシア' , '勝利') , 
('ディヴァイン' , '勝利') , ('リンダ' , '勝利') , ('イリアス' , '勝利');
 ここで問題です。騎士サーラは一体何勝したのでしょうか。この程度なら簡単なものではありますが。
// 勝利数を得る(ちなみに結果は「9」)
SELECT COUNT(*) FROM combat WHERE result = '勝利';

// 相手ごとに勝利数を得る
SELECT enemy , COUNT(*) FROM combat WHERE result = '勝利' 
GROUP BY enemy WITH ROLLUP;

// Result
enemy	COUNT(*)
イリアス	1
シェイン	3
シンシア	1
ディヴァイン	1
ハードゥン	1
ブライトネス	1
リンダ	1
NULL	9
 確かに得られましたが、それなら「総戦闘数がこれだけで、そのうち勝利がこの程度」といったものを簡単に出す方法はないでしょうか。また、上の飲み会の例と同じようなものですが、サッカーのように勝利3点、引き分け1点、敗北0点のような計算をサブクエリレスで、しかも1つのSQLでごく簡単に行うことはできないのでしょうか。
 そんなこんなで、今回適当に考え付いてしまった方法を。
// 勝利数を得るクエリ
SELECT SUM(result = '勝利') FROM combat;

// 戦闘数と諸結果を得るクエリ
SELECT COUNT(*) AS '戦闘' , SUM(result = '勝利') AS '白星' , 
SUM(result = '敗北') AS '黒星' , SUM(result = '引き分け') AS '引分' 
FROM combat;

// Result
戦闘	白星	黒星	引分
12	9	1	2

// それをキャラ別に見るクエリ
SELECT enemy , COUNT(*) AS '戦闘' , SUM(result = '勝利') AS '白星' , 
SUM(result = '敗北') AS '黒星' , SUM(result = '引き分け') AS '引分' 
FROM combat GROUP BY enemy WITH ROLLUP;

// Result
enemy	戦闘	白星	黒星	引分
イリアス	2	1	1	0
サラサーラ	1	0	0	1
シェイン	3	3	0	0
シンシア	1	1	0	0
ディヴァイン	1	1	0	0
ハードゥン	2	1	0	1
ブライトネス	1	1	0	0
リンダ	1	1	0	0
NULL	12	9	1	2

// 勝ち点を出す
SELECT SUM(result = '勝利') * 3 + 
SUM(result = '引き分け') AS '勝ち点' FROM combat;

// Result
勝ち点
29
 この通り、きれいなものです。ふむ、サーラさんは12戦9勝2引き分け、勝ち点29ですか。ごく簡単にこうした結果が得られるのです。サブクエリなど使わせません。頭がこんがらがるSQLですが、このように意外と単純にいくことも多いのです。
// 付録 - 勝率の求め方(サーラさんの勝率は0.75)
SELECT SUM(result = '勝利') / COUNT(*) AS '勝率' FROM combat;

// キャラ別
SELECT enemy , SUM(result = '勝利') / COUNT(*) AS '勝率' 
FROM combat GROUP BY enemy;
 SQLの演算の性質を上手く利用すれば、かなり色々なことができます。これを知っておくのと知らないのでは全く違うばかりか、単純なSQLで実装できるところをわざわざ(4.1以降でしか使えない)サブクエリを使うことにもなりかねません。しかも、サブクエリを使っても実装が著しく長かったり難しかったりするものも。
 やはり「SQLとREGEXは使いよう」なのです。「使っても何もできない」人から「使えばなんでもできる」人まで。多少は何でもできるようになりたいものです。
カテゴリ [開発魔法][社会問題][ゲーム開発] [トラックバック 0][コメント 0]

背筋も凍るお話
2006/03/15(Wed)17:24:32
 先日やりかけになっていた「XPath for Java」。クラス「javax.xml.xpath.XPath」を用いることで、JavaではXPathを使うことができるのですが、これはネームスペースがなければの話であって、ネームスペースを使用する場合はXPathを使うことができません(ちなみにJavaのXMLパーサはデフォルトではネームスペースを無視します。無視させないためには、ファクトリを設定してからDOMオブジェクトを作らねばなりません)。
 ただ、これは正確には「ネームスペースを使うにはインタフェースNamespaceContextを継承したクラスをXPathに渡さなければならないが、J2SE 5.0にこのインタフェースを実装していたり、またはファクトリとなるようなクラスはない」が正解です。もしかしたらApplication Serverにはあるかもしれませんが、少なくともJ2SE本体にはありません。つまり、継承したクラスを自分で作りさえすればネームスペースを使うことはできることになります。
 しかしまあ、このインタフェースの規則のややこしさといったら。ネームスペースURIの正引き・逆引きに対応しなければならないばかりか、W3Cの特定のURIを渡されたら特定のプレフィックスを返したり、逆にそのプレフィックスを渡されたら特定のURIを返したり、getNamespaceURIでは空白を渡されたらデフォルトネームスペースURIを返す必要があるくせに、getPrefixではnullであったりと。
 ところで、このインタフェースにはIteratorを返さなければならないメソッド(getPrefixes)が含まれているのですが、Iteratorはジェネリックスを使っています。それ自体は別に構わないのですが、バグだか仕様だか知りませんが(少なくともTomcat 5.5では)JSP内でジェネリックスのあるクラスを返り値に使うとどういうわけか「非互換」エラーを起こします。色々やってみましたが、今のところ回避できないのが実状です。
 仕方がないのでServletを使って実装しました。これならしっかりコンパイルも通りますし、正しく動作させることができます。しかし、JSPなら作成も検証も楽ですから、できればJSPを使いたかったのですが。便利で不便なジェネリックス。
class Namespace implements NamespaceContext{
	String prefix[];
	String regns[];
	int len;
	public Namespace(){
		len = 0;
		prefix = new String[len];
		regns = new String[len];
	}
	// PHP風に registerNamespace で登録します
	// 引数の順番もPHPにあわせてみました
	// registerNamespace("" , "[URI]") でデフォルトも指定できるはず
	public void registerNamespace(String p , String ns){
		String np[] = new String[len + 1];
		String nns[] = new String[len + 1];
		for(int i = 0; i < len; i++){
			np[i] = prefix[i];
			nns[i] = regns[i];
		}
		np[len] = p;
		nns[len] = ns;

		len ++;

		prefix = np;
		regns = nns;
	}
	// Prefix -> NamespaceURI
	public String getNamespaceURI(String p){
		// xml -> http://www.w3.org/XML/1998/namespace
		if(p.equals(XMLConstants.XML_NS_PREFIX))
			return XMLConstants.XML_NS_URI;
		// xmlns -> http://www.w3.org/2000/xmlns/
		if(p.equals(XMLConstants.XMLNS_ATTRIBUTE))
			return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;

		// 当該ネームスペースを探す
		for(int i = 0; i < len; i++){
			if(p.equals(prefix[i])){
				return regns[i];
			}
		}

		// DEFAULT_NS_PREFIX の場合でデフォルトが登録されていない
		// または登録されていないPrefixが検索された場合
		return XMLConstants.NULL_NS_URI;
	}
	// NamespaceURI -> Prefix
	public String getPrefix(String ns){
		// http://www.w3.org/XML/1998/namespace -> xml
		if(ns.equals(XMLConstants.XML_NS_URI))
			return XMLConstants.XML_NS_PREFIX;
		// http://www.w3.org/2000/xmlns/ -> xmlns
		if(ns.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI))
			return XMLConstants.XMLNS_ATTRIBUTE;

		// プレフィックスを探す
		for(int i = 0; i < len; i++){
			if(ns.equals(regns[i])){
				return prefix[i];
			}
		}

		// 見つからない場合
		return null;
	}
	// 特定URIに対応する全プレフィックスを返す
	public Iterator getPrefixes(String ns){
		Vector<String> v = new Vector<String>();

		if(ns.equals(XMLConstants.XML_NS_URI)){
			v.add(XMLConstants.XML_NS_PREFIX);
			return v.iterator();
		}
		if(ns.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)){
			v.add(XMLConstants.XMLNS_ATTRIBUTE);
			return v.iterator();
		}

		for(int i = 0; i < len; i++){
			if(ns.equals(regns[i])){
				v.add(prefix[i]);
			}
		}
		return v.iterator();
	}
}
 本当はHashtable辺りを使おうかと考えていたのですが、そもそもURIとプレフィックスのどちらをキーにすべきかすら不明ですし、XMLでは1つのURIまたはプレフィックスが複数のプレフィックスまたはURIを持つことがあり、キーと値の対応するHashtableでは限界があると考え、あえてString配列を使いました。スマートではありませんが。本当はnullを渡されたら「IllegalArgumentException」を投げなければならないのですが、割愛。
 で、使用方法ですが、以前に作ったXML(アビリティリスト)を流用して試してみます。
// ファクトリを準備
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

// 重要 : ネームスペースを有効にする
dbf.setNamespaceAware(true);

DocumentBuilder db = dbf.newDocumentBuilder();

// XMLデータを読み込み
Document d = db.parse("webapps\\ROOT\\ns.xml");

// ファクトリからXPathを取得
XPath xp = XPathFactory.newInstance().newXPath();

// ネームスペースを作る
Namespace ns = new Namespace();
// なんともPHPライクな登録方法
ns.registerNamespace("ilias" , "http://www.yamicha.com/ilias");
ns.registerNamespace("sara" , "http://www.yamicha.com/sara");
xp.setNamespaceContext(ns);

// 魔道士イリアスのアビリティの中でMR消費の値を返す
NodeList n = (NodeList)xp.evaluate("//Base/ilias:Ability/@ilias:use" 
	, d , XPathConstants.NODESET);
out.println("該当個数 : " + n.getLength());
for(int i = 0; i < n.getLength(); i++)
	out.println(n.item(i));

// 同じく魔道士イリアスのアビリティの中でLA消費の値を返す
n = (NodeList)xp.evaluate("//Base/ilias:Ability/@sara:use" , 
	d , XPathConstants.NODESET);
out.println("該当個数 : " + n.getLength());
for(int i = 0; i < n.getLength(); i++)
	out.println(n.item(i));
 これを実行すると、以下のような結果が得られます。
該当個数 : 3
ilias:use="108"
ilias:use="86"
ilias:use="126"
該当個数 : 1
sara:use="200"
 どうやら正しく動作しているようです。めでたし。
 ところで、J2SE 5.0ではこれまで扱ったアノテーション、ジェネリックスなどの他、foreachループもサポートしています。無論、知らなかったわけでも力量が足りなかったわけでもなく、利点もなく面倒だから扱わなかったに過ぎません。せっかくですから扱ってみます。
 foreachといってもPerlやPHPのようにforeach文が用意されているわけではなく、forループを簡素化しただけです。配列は最初から、クラスに関してはインタフェースiterableを実装したものに関しては同様にforeachループを用いることができます。その意味で、foreachループには2種類あることになります。
 ただ、「使うことに意義がある」とさえいえる、PerlやPHPの非常に有用なforeachとは違い、Javaのforeachは単にコンパイラが「従来までのループ」と「新しいforeachループ」の2つの書き方をサポートしたに過ぎません。ですから、配列の値を取り出した上にインデックス数と一緒に表示したいような場合は、従来のforを使った方がよっぽど楽です。
 というわけで作ってみた配列版foreachです。
String names[] = 
	{"サーラ" , "サラサーラ" , "サラサラサーラ" , "(サラ){4}サーラ"};

for(String name : names){
	System.out.println(name);
}

// これまでの書き方なら
for(int i = 0; i < names.length; i++){
	System.out.println(names[i]);
}
 別にそんなに代わり映えするものでもなし。後者の方がJavaっぽいですし(どういう理由だか)、何より後者の方法さえ使っていれば、5.0以前のJavaでも正しくコンパイルすることができます。手間が激減するのであればともかく、互換性を考えれば、わざわざforeachループを使うまでもないでしょう。
 もう1つ、忘れてはいけないのがクラス版foreachループです。結局のところIteratorを順番に取り出してループしているだけであって、そこまですごいものではありません。
// foreachに対応したクラスを作る
class ForLoop<T> implements Iterable<T>{
	T values[];
	int len;
	public ForLoop(T val[]){
		values = val;
	}
	public Iterator<T> iterator(){
		ArrayList<T> al = new ArrayList<T>();
		for(T value : values)
			al.add(value);

		return al.iterator();
	}
}

// 使い方
// ジェネリックスなので何型でも渡せるが、とりあえず先ほどのデータ(String)を
String names[] = 
	{"サーラ" , "サラサーラ" , "サラサラサーラ" , "(サラ){4}サーラ"};

ForLoop<String> fls = new ForLoop<String>(names);
for(String fl : fls){
	System.out.println(fl);
}
 どちらも結果は同じです。ForLoopクラス内でも、さりげなくジェネリックスと配列foreachを活用しているのが私らしいところ。キャストなどという無粋なマネはしません。
 さて次、PostgreSQLではトリガでストアド プロシージャを呼んだりするらしいですが、MySQLでは実装中とのこと。とはいえ、これこそ「やればできる」というものでして。やってできないことはない、とはいえMyISAMでトランザクションを使ったりすることは絶対に無理ですが、ストアド プロシージャを呼ぶ程度なら何とか。
// 更新時刻格納テーブル
CREATE TABLE updatetime(name VARCHAR(256) , dates DATETIME , INDEX(name));

// トリガを設定するテーブル
CREATE TABLE trigsample(number INT , name VARCHAR(32) , PRIMARY KEY(number));

DELIMITER //

// 時刻を記録するプロシージャ
CREATE PROCEDURE updateprocedure(IN name VARCHAR(256)) 
BEGIN 
 INSERT INTO updatetime VALUES(name , NOW());
END;//

// そのプロシージャを呼び出すトリガ
CREATE TRIGGER updatetrigger AFTER INSERT ON trigsample FOR EACH ROW 
BEGIN 
 CALL updateprocedure('trigsample');
END;//

DELIMITER ;

INSERT INTO trigsample VALUES(0 , 'Dual Knight') , (1 , 'Sword Master');

SELECT * FROM updatetime;

// Result
name	dates
trigsample	2006-03-15 15:03:06
trigsample	2006-03-15 15:03:06
 これで結構何とかなってしまいます。なお、トリガでトランザクションを使うことはできないようです。ストアド内ならできるにもかかわらず。ストアド ルーチンでは「BEGIN」のワードが使えないため、「START TRANSACTION」します。
DELIMITER //

CREATE PROCEDURE transactprocedure() 
BEGIN 
 START TRANSACTION;
  SELECT * FROM sample FOR UPDATE;
  INSERT INTO sample VALUES(128 , yamicha.com');
 ROLLBACK;
END;//
 こういうのを普通にコールすれば正しく動くのですが、トリガで呼び出すとエラーになります。大体MySQLではトランザクションをネストできませんから、トリガがトランザクション内で実行されていたら色々面倒なことになるのでしょう。

 またも笑い話、東京教育委員会が国旗国歌を厳しく強制しています。生徒の起立なしなら教師の処分もあるとか。教師を脅して生徒の行動を強制させようという魂胆ですが、私が生徒ならまず100%起立しません。教師・先生は数あれど、私の恩師は自動車学校の2名の教官と学科教官以外には1人たりとも存在しません。残念なのは、私が生徒でないことですが。
 私は法案の起こりからして国旗国歌法案に強く反対しています。以前、国旗国歌に関する圧力によって校長が自殺したことがありました。これを受けて政府が同法案を出したのですが、見出しだけを見た時の私の感想は「ああ、やっぱり」でした。もう2度とこのような悲劇を繰り返してはいけないのですから。
 ところが、記事内容を読んでビックリ。私はてっきり「国旗国歌強制で死人が出たのだから、これからは学校の裁量にしよう」という法案を想像していたのですが、何と正反対。もうあきれ果てて物も言えませんでした。何というサル知恵。それ以来、私は「国旗国歌法案絶対反対」を貫いています。国旗国歌の理念はこの際どうでもよろしいですし、そんな偉そうな能書きなどいちいち気にしているほど余裕も暇もありませんが、こういう法律の作り方だけは許せません。
 さて、ここで(寒さが戻ってきただけに)納涼怖い話を1つ。いよいよもって鳥インフルエンザ発生の可能性が高くなっています。WHOも会合を開いたりと色々活動を行っているようで、かなり危険が強くなっていると考えて良いでしょう。SARSのように立ち消えになってくれるのが最も良いのですが。
 ウィルスというのは非常に変異しやすいものですから、条件さえ重なればあっという間に人間に感染するようになるでしょう。感染力がどの程度かは実際に変異ウィルスが出てこないことには分からないのですが、人間が新ウィルスに対する免疫を持っていない以上、感染すると高い確率で発病することになります。
 そして、このウィルスの何が怖いって、免疫の自己攻撃を誘発する点でしょう。普通のインフルエンザの場合、大流行が起きるとお年寄りや乳幼児などを中心に重篤化・死亡例が出たりしますが、鳥インフルエンザが怖いのは年齢に関係なく高い致死率を持つ点であり、これの原因が「免疫による自己攻撃」とされています。抵抗力が高ければ安全とは言い切れません。
 はっきり言って病原体は人類を存亡の危機に追いやるほど恐ろしいもので、それこそイラクくんだりに時間と人員と資金を浪費している場合ではありません。まだ人対人のウィルスでない鳥インフルエンザがこれだけ騒がれていることを見ても明らかです。もともと血液型が色々あること自体、例えば特定の血液型への致死率が高い病気が流行したような場合、人類が存続する可能性を高めるためといわれていますから。
 普通のインフルエンザにしても、日本ではバカのようにタミフルを乱用していますが、タミフル耐性インフルエンザはきっとすでに存在します。タミフルなど飲まなくていい人間が贅沢病でタミフルを飲むから、その結果としてインフルエンザを強化させ、それが老人や病人などタミフルなくしては生命の存続すら危うい人に感染したりするのです。窓から飛び降りるなどの副作用の可能性がある事例を考えても、飲まなければ生命などが脅かされる場合を除き、私はタミフルを飲むことは避けたいです。
 ついでに言っておけば、ウィルスは遺伝子を交換して性質を変えたりするそうですが、鳥インフルエンザがタミフル耐性インフルエンザの遺伝子をもらったらどうしますか。日本の「何ら問題なく治癒する病気に対して薬を乱用する」という贅沢病、下手すると人類の敵になる可能性があります。
 怖いのはインフルエンザばかりではなく、生物兵器に天然痘が用いられたら、といった試算もあります。それならまだかわいいもので、抗生物質耐性結核が流行し始めたら手に負えません。薬があるから結核は簡単に治癒するのであって、これに耐性を持たれて手も足もでなくなってしまえば、結核は死病に逆戻りです。
 結核は菌が起こす病気ですが、これの治療方法としては、2種類またはそれ以上の薬をしばらくの間毎日飲むのが標準的なのだそうです。しかし、薬をいい加減に飲んでいると薬に耐性ができてしまうとのこと。細菌でさえこの調子なのですから、もともと変異しやすい性質を持つウィルスならどうなることか。
 ついでに異常プリオンも恐ろしいこともお忘れなく。「遺伝子のない病原体」などといわれますが、本当に遺伝情報がないのか、それならどうして病原性を持つのか、といったことは未だはっきりとは解明されていません。仮に異常プリオンが遺伝情報を隠し持っており、何らかの方法で人から人に感染するようなことがあれば、それこそどうなるか。
 病気というのは恐ろしいです、ホント。事件の事故のと毎日大騒ぎですが、死者数で見れば病気が最も高いのですから。

 ところで、何の因果か変な夢を見ました。何やら「Typing Wind 2」というものがあり、それをプレイしている夢です。はっきり言ってタイピング系のプログラムは現在まで企画案にすら入っていませんでしたが、少しばかり変な気分です。
 内容としては、夢だけに夢と消えてしまったのですが、まあタイピングです。しかし、戦略タイピングとでも名づけましょうか、タイピングの速さと戦略で戦うようなゲームでした。夢では魔道士イリアスか誰かが戦っていました。片方がイマイチでももう片方である程度カバーできるわけです。ある意味私らしいゲームですが、せっかくならソースまで見せてくれればよかったものを
 そこで考えたのが、例えば単語をタイプしたら速度に応じてMRが蓄積し、それを使って攻撃などの行動ができる、といったゲームですが、あまり面白そうではありません。com.yamicha.romanxのパッケージを使えば作れるのですが、おぼろげには見えているものの骨組みが全く決まらず、実装に入れないためもやもやした企画がこれで3、4個。
 さて、どうしたものでしょう。しかもSQLもXMLもちょくちょくやらなければ知識が失われてしまいますし。難しいところです。
カテゴリ [開発魔法][社会問題][ゲーム開発] [トラックバック 0][コメント 0]

規制緩和も解除しては
2006/02/27(Mon)20:07:59
 以前のFTPの際、せっかくメールサーバーを導入したのに、DNSがないのでは使うことができません。しかもイントラネットといえば、やはりDNSサーバーなしには考えられません。そこでDNSサーバーの世界標準であるBINDを導入しました。
 まずドメインやIPの基礎知識ですが、これは5年ほど前に押さえておいた知識が役に立ちました。192.168がクラスCのプライベート、127.0.0.1がループバック、DNSサーバーでIPをドメインに変換する、アドレスのクライアント部分が全部0または255になるアドレスは使えない、0はブロードキャスト用に予約、それゆえにクラスCでは256ではなく254のアドレスを使うことができる、10だの172だのといった意味不明な数値を覚えたり、ビットマスクを考えたりするのは、初めてだとかなり骨の折れる作業です。
 しかし、一言でDNSを導入するといっても、これの難しさといったらありません。BINDはデフォルト設定では全く動作せず、設定を全部自分で作らなければなりません。しかも最終的に大量の設定ファイルが必要になります。今回行ったのは「内向きDNS」というもので、LAN内のIPにドメインを割り振るためのものですが、インターネットのドメインも原理は同じです。
 今回は「192.168.0.1」に「localyamicha」というドメインを割り当てました。つまり、Apache、War FTP、Mail Serverを起動しておけば、このドメインを使ってサーバーにアクセスしたり、FTPにアクセスしたり、メールを読み書きしたりできることになります(無論LAN内でのみ。新しくドメインを取るか、またはyamicha.comドメインをこのPCに割り当てれば、インターネットでもこれができるようになります。固定IPでなかったり、セキュリティが甘かったり、第一サーバーがポンコツなので、やる気はありませんが)。
 とりあえず、その道のページをいくつも探して読み漁った上、それらの情報を全部照合して色々検討し、何とか設定してみました。まずはBINDを「C:\Windows\System32\dns」にインストールし、その中の「ext」にファイルを作って色々設定をすることになります。これがとにかく大変、ユーザーに優しくありません。今回は以下の通り設定しました(実際にはkeyなども作りましたが、ここでは最小限の部分だけ)。また、ファイルの最後には必ず改行を入れなければなりません。これがなければエラーになってしまいます。
// 以下すべてのファイルを ext に作成し、テキストエディタで編集

// ファイル named.conf
acl lan{
	192.168.0.0/24;
	127.0.0.1;
};

options{
	directory "C:\WINDOWS\SYSTEM32\dns\etc";
	// 人によっては C:\WINNT\... かもしれません

	// 範囲をLANのみに限定
	allow-transfer{ lan; };
	allow-query{ lan; };
};

logging{
	channel default-log{
		file "C:\apache\logs\named.log" versions 5 size 1m;
		// ログファイルの場所。実際はどこでも構いません
		// 私はログの場所をまとめるためにApacheのlogsにしました
		severity info;
		// ログの出力レベルです
		// 上手く動作しないなら info を debug にしてログチェック
		print-time yes;
		print-category yes;
	}; 

	category lame-servers{ null; };
	category default{ default-log; }; 
};

view "local"{
	match-clients { lan; };
	// ROOT
	zone "." IN{
		type hint;
		file "named.root";
	};

	// 逆引き loopback
	zone "0.0.127.in-addr.arpa" {
		type master;
		file "named.local";
	};

	// 正引き : domain -> IP
	zone "localyamicha.com" {
		type master;
		file "localyamicha.com.zone";
	};

	// 逆引き : IP -> domain
	zone "0.168.192.in-addr.arpa" {
 	         type master;
 	         file "192.168.0";
	};
};

// ファイル 192.168.0
// webmaster.yamicha.com の部分はメールアドレスになります
// ただし @ は . に変更します
// 1	IN PTR... の部分の「1」はIP末尾の数値になります
// 例えばIPが「192.168.0.128」なら「128」に設定します
$TTL 86400
@	IN SOA	localyamicha.com. webmaster.yamicha.com. (
	2006022700 3H 15M 1W 1D )

	IN NS    localyamicha.com.
1	IN PTR  top.localyamicha.com.

// ファイル localyamicha.com.zone
// top や www はいわばサブドメインのマップです
// @ はルートを表します
$TTL 86400
@	IN SOA   localyamicha.com. webmaster.yamicha.com. (
	2006022700 3H 15M 1W 1D )

	IN NS	localyamicha.com.
	IN MX	10 localyamicha.com.

@	IN A	192.168.0.1
top	IN A	192.168.0.1
www	IN CNAME	top
mail	IN CNAME	top
ftp	IN CNAME	top

// ファイル named.local
$TTL 86400
@	IN SOA   localyamicha.com. webmaster.yamicha.com. (
	2006022700 28800 14400 3600000 86400 )

	IN NS	localyamicha.com.
1	IN PTR	localhost.

// ファイル named.root
※このファイルは特殊で、自分では作る必要がありません。
InterNICから最新版をダウンロードすることが必要です。
 ドメインの後の「.」を忘れてはいけません。ドメインは逆に解釈されるものであり(例えば void.yamicha.com は comの中のyamichaの中のvoid)、これらのトップレベルのドメインは「ルート」です。すなわち、例えば「www.yamicha.com.」という記述は「(ROOT)/com/yamicha/www」を表しているのです。
 とりあえず、ここまでで設定は完了です。後はサービス「ISC BIND」を起動してやれば、この状態でDNSは有効になっていますし、メールサーバーさえしっかり設定すればメールの送受信を行うこともできます(例によってLAN内でのみ)。しかし、ブラウザにドメインを打ち込んでもサイトを開くことは当然できません
 私の場合はADSL環境を用いているのですが、この接続に対してDNSの設定を行うことが必要になりました。具体的には接続設定の「TCP/IP」のプロパティを開き、「DNSサーバー」に自分が立てたDNSサーバー(私の場合は 192.168.0.1)を入力すれば良いのですが、これだとDNSサーバーを落とした時にサイトが見られなくなります。
 かといって自前のDNSが使えないのでは意味がありませんから、ここは「優先DNS」にISPから割り当てられたDNSサーバーのIPを指定し(アドレスは ipconfig /all で見られます)、セカンダリに「192.168.0.1」を割り当ててみました。私のように「localyamicha.com」なる他に取る人がいないであろうドメインを使うなら、これで十分でしょう。
 これでようやくDNSが使えるようになり、メールもLAN内でのみ送受信ができるようになりました。メールサーバーにアカウントを作っておきましたので、「yamicha@localyamicha.com」で送ればメールボックスにメールが蓄積され、送受信して受け取ることができます。
 そこで、記念にPHPでメールセンダーを作ってみました。PEARライブラリのMailを用いれば、Sendmailなしでもメールを送信することができます。ただし、smtpを使ってメールを送信する場合、別途「Net_SMTP」「Net_Socket」PEARをダウンロードし、この両方を「PEAR/Net」ディレクトリに投げ入れなければなりません。
 今回作ったPHPは以下の通りです。このライブラリはSMTP認証にも対応しています。
require_once 'Mail.php';

// SMTPサーバーを指定
$params["host"] = "mail.localyamicha.com";

// SMTP認証(メール送信時認証)を行うか
$params["auth"] = true;

// 行う場合はユーザー名とパスワードを指定
$params["username"] = "yamicha";
$params["password"] = "password";

// 送信方法(今回はsmtp)とデータを渡す
$mail = Mail::factory("smtp" , $params);

// メールのヘッダを作る
$headers["From"] = 'yamicha@localyamicha.com';
$headers["To"] = 'yamicha@localyamicha.com';
$headers["Subject"] = 'Test';

// 送信
$mail->send("yamicha@localyamicha.com" , $headers , "本文");
 これでメール送信は完了です。PEARさえ設定すれば簡単なものです。
 FTPは「ftp://localyamicha.com/」で動いてしまいますし、「http://www.localyamicha.com/」ならApacheのhtdocsのindexが表示されますし、これは便利です。

 それにしても最近、どうもSQLを扱う用件がありません。MySQLに関してはほぼ全部の機能を使ってしまったので当然ではありますが、これまでの開発の記憶の中で最もデリゲートデリケートな記憶はSQLです。構文が独特な上、「FOREIGN KEY(keyname) REFERENCES tablename(keyname) ON DELETE NO ACTION ON UPDATE CASCADE」「SET TRANSACTION ISOLATION LEVEL READ REPEATABLE」といった具合にひたすら長く複雑ですから(何とかここまではそらで出てきました)、少し離れるだけであっという間に忘れてしまいます。

 またもやメール問題ですが、「民主党は謝罪で収拾の方向」とのこと。これが適切かどうかは分かりませんが、少なくとも総辞職まで必要な状況ではないのは事実です。なぜなら、白黒はっきりさせるために国政調査権を発動すべきところを、自民党がそれを頑なに拒み、身の潔白を完全に証明していないためです。
 メールは偽物の可能性が高くなっていますが、内容が偽物とは限りません。これを証明するためには国政調査権の発動が必要でした。ところが、自民党がそれをしなかった以上、疑惑を黙認したとみなさなければなりません。メールは灰色、自民党の行為も灰色ですから、現状では民主党が100%悪いわけではありません。
 自民党が身の潔白を証明して初めて、メール問題は完全に「偽情報を基にした民主党の勇み足」となります。そうなれば私も前原執行部の辞任を求めますし、それをしない民主党など見限ります。しかし、この疑惑は灰色です。自民党には調査権を発動できない理由があるのですから仕方ありません。もし可能であれば、今からでも遅くはありませんから、自民党は国政調査権の発動を検討すべきなのですが。
 何にせよ、民主党の「自爆攻撃」は自民党の外壁を吹き飛ばし、「国政調査権を発動されては困るらしい事実がある」ことを明らかにしてくれました。ここで民主党が腰砕けになってしまっているのが最大の問題なのですが、自らのダメージも省みずにこれをガンガン追求して真相を明らかにする程度の気概がなくては政権は取れません
 いささかやり方はマズかったですが、自民党に「刑事訴追の恐れがありますので証言を拒否します」と言わせたのですから、これだけでもなかなかの収穫でしょう。

 こちらの方が重要ニュースなのですが、政府もいよいよ日銀の量的緩和解除を容認することに言及しました。日銀の量的緩和といえば不況の象徴のようなものですから、これが解除されるとなれば本当に日本の足腰はしっかりしてきたことになります。
 そもそも量的緩和とは何ぞや。以前にもブログで取り上げたような気はしますが、金融関係の用語というのはやたら複雑でいけません。いちいち正面から説明しても分かりづらいことこの上ありませんから、即席で考えた例え話でも。
 現在、日本は自転車の練習をしています。補助輪がついたり、人に押さえてもらっていたりしています。しかし、この状態では確かに倒れることはありませんし、安定もしているのですが、これでは自転車を乗り回したりすることはできません。だからこそ、補助輪や他人に手伝ってもらうなどして練習をするわけですが。
 量的緩和解除といいますのは、言ってみれば「補助輪を取る」「人の手伝いなしで乗ってみる」に当たります。今までの練習で十分に鍛えられていれば、自転車で自在に走ることができるようになります。しかし、練習が足りないままこれをすると転んでしまうのです。自転車に補助輪をつけるのと同様、量的緩和も過渡的措置であり正常な状態ではありませんから、いつかは解除しなければなりません。
 この不況はそもそも、それなりの時期に回復するはずでしたが、せっかくの回復が橋本消費税によって叩き落されたところから始まります。実は以前、日銀は「景気はもう回復した」と補助輪(ゼロ金利)を外したことがあったのですが、それで日本は派手に転んでしまい、景気が失速してしまいました。日銀にはこのトラウマがあるのです。
 そこで、日銀も政府も慎重になり、「補助輪を外すべきか、まだやめておくか」と思案している状況です。十分に自転車を運転できる技術が身についているのなら、補助輪や人の助力など逆に景気回復を阻害する足かせにもなりかねないものですが、自転車を使えないのにそれらをやめた場合、せっかくの回復基調の時にまたもや転んでしまう恐れがあります。
 政府がこれを容認した以上、政府にもある程度の確証があるのでしょう。目が離せなくなってきました。

 ついでに先日のSLGをプレイしてみたのですが(作った直後では気づかない点が多いため、1日置いてプレイしてみることは多いです)、面白いです。20ターンがかなりピッタリです。魔道士イリアスの「マキシマムライフ」の意地悪さといったら。回復の射程が広いので散り散りになってしまい、魔法で一掃できないのも辛いところです。
 MRを蓄積しつつ「波動諷詠剣」と「カウンター」を主体に攻め、MRが十分に蓄積したところでエクスクルージョンで倒せる相手がいるなら周りを巻き込んでエクスクルージョン、を繰り返して地道に戦力を削っていく戦法を採りました。相手の攻撃は、最もダメージが大きい「ルイネッドワールド」でも200程度にしかならないため、防御は全く考えません。
 そうしてジリジリ攻めていけば、まずアークパラディンや剣聖を倒すことができます。これだけで相手の回復力はぐっと下がりますから、引き続き戦力を切り落としていきます。革命魔道士は「リカバリー」「マキシマムライフ」ともに強烈な上、魔法も効きづらく苦戦するのですが、こちらはとにかく後回しにして他から削ります。
 あらかた削れたら、残りはデュアルナイトと革命魔道士になるでしょう。デュアルナイトは魔法が効くため、後の問題は革命魔道士です。戦い方は前と同じく、相手のLAを削りつつMRを蓄積します。MRが十分な数値になったら、相手のLAをある程度まで減らした上で、背後からのデストラクション1発で一気に撃破します。これで勝利することができました。
 ただ、戦略の幅を持たせるために「デュプリケーション」も持たせてあります。「リカバリー」「ディスパッチウィンド」「キュアーライト」は使う機会がなさそうですが、デュプリケーションはかけておけばしばらく2回攻撃、2連カウンターが可能になります。ただ、魔法の場合はMRを2回分消費(足りなければ2度目が発動しない)してしまうため、実用的ではありません。この辺りの駆け引きが面白いです。
 20ターンを終えると普通のデュアルナイトに戻りますが、万が一撃破が間に合わなかった場合、とりあえず有利な地形に陣取って戦うしかないでしょう。残った相手が革命魔道士なら、頑丈な上に回復まで使ってきて最悪ですから、革命魔道士だけはしっかり倒しておきましょう。
 それにしても今回も「リンダ・デミケル」だの「マリー」だの。幻影の名前はどういう基準でつけられるのでしょうか。「サラサーラ」というのもアレですし。「朝鮮総カレン」はさすがにかわいそうなのでは。ちなみに「ロンホー」(栄和)というのは麻雀のロン上がりです。
カテゴリ [開発魔法][社会問題][ゲーム開発] [トラックバック 0][コメント 0]

[1] [2] [3] [4] [5] 次のページ->
- Blog by yamicha.com -