No Such Blog or Diary

«Prev || 1 | 2 | 3 |...| 56 | 57 | 58 || Next»

自己出力プログラムの生成プログラム

Java のソースを読んでそのソースを含む自己出力プログラムを吐くプログラムを書いた.あまり美しくないなぁ...

/***
 * 次の Self2.template をソースファイルに埋め込んで,
 * そのソース全体を含む自己出力プログラムを作成する.
 * 実行クラス名は SelfPrint (Self2.template のクラス名) となる.
 * \r は置き換えないのでソース中に含まないようにすること.
 * \n, \t, " を %n, %t, %c という文字に変換して文字列を埋め込むので,
 * ソースはこれらを含まないこと.
 * あと, %s もあるとまずい.
 * エスケープ文字もまずそう.
 * Self.template の main 関数で何かメソッドを呼ぶように書けば仕事もできる.
 * そのばあい,画面出力があると自己出力の意味が無いけど...
 *---------------------- Self2.template start -------------------------- 
class SelfPrint {
	static String s="%s";
	public static void main(String [] args) {
		String ss=s.replaceAll(new String(new byte []{37, 110}), new String(new byte []{10}));
		ss=ss.replaceAll(new String(new byte []{37, 116}), new String(new byte []{9}));
		ss=ss.replaceAll(new String(new byte []{37, 99}), new String(new byte []{34}));
		ss=ss.replaceAll(new String(new byte []{37, 115}), s);
		System.out.print(ss);
	}
}
 *---------------------- Self2.template end -------------------------- 
 *
 */
 
import java.io.*;
 
class SelfGen2 {
	public static void main(String [] args) throws Exception {
		if(args.length <= 0){
			System.out.println("Usage: java SelfGen2 input.java");
			System.out.println("Note: input.java should not contain CR");
			System.exit(0);
		}
		File file = new File(args[0]);
		FileReader fis = new FileReader(file);
		int len = (int)file.length();
		char [] buf = new char[len];
		len = fis.read(buf, 0, len);
		String source = new String(buf, 0, len);
		File tfile = new File("Self2.template");
		FileReader tfis = new FileReader(tfile);
		int tlen = (int)tfile.length();
		char [] tbuf = new char[tlen];
		tlen = tfis.read(tbuf, 0, tlen);
		String template = new String(tbuf, 0, tlen);
		source = source + template;
		//System.out.println(source);
 
		String ss = source;
		ss=ss.replaceAll(new String(new byte []{10}), new String(new byte []{37, 110}));
		ss=ss.replaceAll(new String(new byte []{9}), new String(new byte []{37, 116}));
		ss=ss.replaceAll(new String(new byte []{34}), new String(new byte []{37, 99}));
		source=source.replaceAll(new String(new byte []{37, 115}), ss);
		System.out.print(source);
	}
}

フック

ふとフックが気になって Windows のフック登録の API を調べてみた.どうやら SetWindowsHookEx という名前らしい.グローバルにフックするにはDLLを作る必要があるそうで面倒かもしれないけど今度これで遊んでみよう.ここらへんが参考になるかなぁ?

Rubyでカウンタ作り

寝すぎて寝れなくなったのでおもむろに Ruby でカウンタを作ってみた.Ruby を選択した理由は単に Perl を使いたくなかっただけ.

そんでさっさと組み上げたわけだけど久々の Ruby で何箇所か引っかかったので挙げてみよう.

  • 配列の可変長引数としての展開は method(*array) のように * をつける
  • インクリメント ++ は無い ( i += 1と書け )
  • CGI 関係は require 'cgi' で (cgi-lib は古い)
  • cgi にはオフラインモードがある (ruby -r cgi prcounter.rb みたいに使う)
  • 文末にセミコロンいりません
  • 文字列つなげるとき + は文頭にかけません.文末に書け

などなど下らないことで時間を食いまくりましたとさ.

記念にできたものはここにおいておこう prcounter-0.10.zip

ちなみにこのカウンタはひとつの数字に対して複数のイメージを対応付け,それをランダムに選択して表示できるという少々変な機能が付いていたりする.実用上はまったく意味がなさそうだなと思いつつ個人的に欲しかったのでつけてみた.

複数ファイルにリダイレクト

グリチャレの動作テストで複数ファイルに入力をリダイレクトしたかったので自力でスクリプトでも書こうと思ったがとりあえずコマンドを調べてみた.すると tee とかいうコマンドがあるようで,これを使ってみることに.使い方はいたって簡単で

tee file0 file1 file2 ...

とすると標準入力が file0 file1 file2... に書き出されるらしい.

とりあえずこれを使ってマスタプロセスの出力を複数のfifoに書き出せたので,ローカルでも複数のクラスタを用いた状態を簡単に構成できた.さーて,テストするか.

if を消去

グリチャレのプログラムをさらに速くしようと,徹夜で研究室のドクターと一緒にプログラムを改良してみた.

ビット操作でちまちまやっていたのを一部バイト操作に置き換え高速化していたのだが,とりあえずそれを全体的にバイト操作にすることで倍以上のスピードアップを達成した.これに関しては誰もが思いつく単純な改良であり,これだけではつまらないと

少々壊れ気味の我々は続いてif 文を殲滅するという奇策に走った.よく知られていることだが,if 文とかでちまちまジャンプしまくるプログラムは遅くなる場合が多く,無駄な if を消すことは速度向上の上でとても好ましい(分岐したときにパイプラインで処理されてたものを破棄しなければならないし).

というわけで我々も無駄な if を探してみたのだが,既にかなり最適化したつもりのコードなのでそんな無駄な if はもう既にない.でもまあ,必要そうに見える if はまだごろごろいるわけで,そいつをどうにかして殺したい衝動に駆られるのは自然な流れである.そして,我々はいくつかの必要そうな if を愉快な方法で抹殺することに成功した.

抹殺例の一つは以下のような感じである.この次の if で分岐する部分が,

     if(cnt < rem){
       *p = (*p << cnt) | (((1<<cnt) - 1) * bit);
       rem -= cnt;
    } else {
     *p++ = (*p << rem) | (((1<<rem) - 1) * bit);
     cnt -= rem;
     rem = 8-cnt & 0x7;
 }

次のようなわけのわからないコードに置き換わった.

    const int min = cnt - ( ( - 1 + ( ( unsigned ) ( cnt - rem ) >> 31 ) ) & ( cnt - rem ) );
    rem -= min;
    cnt -= min;
    const int isRem0 = ( -1 + ( ( unsigned ) ( -rem + 1 ) >> 31 ) ) & ( -rem + 1 );    
    *p = ( *p << min ) | ( ( ( 1 << min ) - 1 ) * bit ); p += isRem0;
    rem += isRem0 * ( 8 - ( cnt % 8 ) );

これにより if 文が抹殺され,実際に実行時間も少しばかり(このルーチンのクロック数で半分くらい)速くなった.

ここでの肝は, signed の x に対して, max(x, 0) = (-1 + ((unsigned)x>>31)) & x のようにif を用いずに max を表現できることだったりする.上の例ではこれを改良して, x>0 なら 1 を,そうでないなら 0 を返す演算を記述している.

このように,あほな努力でちょっとでも速くてとても読みにくいコードが出来上がるのはとても面白い.

C#のお勉強をかねた「最萌カウンタ」

C#を勉強するために何かアプリケーションを作ろうと思い第2回東方最萌トーナメント の集計ソフトの GUI 版を作成してたんだけど,ようやく納得の行く動作のものが出来上がった.

もう,リストアイテムのソートがアルファベット順しかできないだの,アイテムが選択されているのに選択されていないだの,エラーをはいて止まるところをエラーを吐かずに止まったり,コントロールの扱い方がちぐはぐだったりとむちゃくちゃ作成に時間がかかった.最大の原因は MSDN のドキュメントが読みにくいことだろうけど... さらには簡単に使える汎用のレイアウトマネージャがないし... まあ,GUIは GUIのデザイナで作るものなのだろうけど.

そんなこんなで結構な時間が過ぎてしまったのだけど(先週のゆゆ様の試合に間に合わなかった...).デリゲートを使ったイベント処理やら各種プロパティの扱い方など,C#でのプログラミングというものを少しは理解できた.でもまあ,なんとなく Java ほどの完成度ではないなぁというのが素直な感想だろうか?もう少しコントロールを共通に扱えるようにしてほしいなぁ,とか.

とりあえず,ここに出来上がったものをおいとこう.いつの間にやら 3500 行とか行ってるし... saimoe-csharp.zip

«Prev || 1 | 2 | 3 |...| 56 | 57 | 58 || Next»
Search
Feeds

Page Top