続★ラクダのステーキ

日々PERLの調べ物などしたことがらを中心に、つれづれなるままに色々と書き連ねる日記。

MFC覚書2:ヒストグラム

2004年09月22日 01時43分10秒 | VC++の勉強
調子に乗って第2弾。


【課題2】ヒストグラムプログラムの作成

ファイルを1バイトずつ読み出し、ファイルの中で0〜255の値が使われている頻度を棒グラフで表示する。
また個々の数値が使われている頻度をローカルにテキストとして保存もできるようにする。
学習内容としては「ドキュメント・ビューアーキテクチャー」のさわりと、「オブジェクトのシリアル化」あたり。


(手順1)
MFC AppWizard(exe)でプロジェクト名を「MyHist」とする。
ステップ1/6でSDIを選択。あとはデフォルトのままでOK。プロジェクトファイル一式を生成する。


(手順2)
ドキュメントを操作するクラス(CMyHistDocクラス)にメンバ変数を追加する。
ClassViewのペインの中のDocに該当するクラス(ここではCMyHistDoc)上で右クリックし、
「メンバ変数の追加」をクリック。下記の2つの変数を追加する。

・int Counter[256]  (データの出現頻度を記録する配列変数)
・CString FileName  (ファイル名を保存する変数)

 ※ここでは話を簡単にするためPublicとしている。


(手順3)
上記2つの変数をCMyHistDocのコンストラクタ内で初期化しておく。

/////////////////////////////////////////////////////////////////////////////
// CMyHistDoc クラスの構築/消滅

CMyHistDoc::CMyHistDoc()
{
	// TODO: この位置に1度だけ呼ばれる構築用のコードを追加してください。

	FileName = "";
	
	int i;
	for(i=0;i<256;i++){
		Counter[i]=0;
	}
}
/////////////////////////////////////////////////////////////////////////////

 


(手順4)
次にCMyHistDocのメンバ関数Serializeを編集する。
これはもともとCObjectの仮想関数として定義されているもので、MFCプログラミングでオブジェクトの内容をファイルに読み書きする時に使う「お約束」らしい。SerializeはArchiveクラスのオブジェクトを参照として渡され、これを使って色々なファイル処理を実装する。
またArchiveオブジェクトは内部でCFileクラスのオブジェクトと連携しているらしく、CFileクラスのメンバ関数を使うと更に細かく色々なファイル処理ができるとのこと。

/////////////////////////////////////////////////////////////////////////////
// CMyHistDoc シリアライゼーション

void CMyHistDoc::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// TODO: この位置に保存用のコードを追加してください。
		int i;
		char s[20];
		// ファイルにデータを書き出す
		ar.WriteString(FileName);
		for(i=0;i<256;i++)
		{
			wsprintf(s,",%02X=%d回n",i,Counter[i]);
			ar.WriteString(s);
		}
	}
	else
	{
		// TODO: この位置に読み込み用のコードを追加してください。
		CFile *cf;
		int i,FileLen;
		unsigned char BinData;

		// カウンタの値を初期化
		for(i=0;i<256;i++)
		{
			Counter[i]=0;
		}

		// ファイル名と大きさを取得
		cf=ar.GetFile();
		FileName=cf->GetFileName();
		FileLen=cf->GetLength();

		// ファイルのデータを読み出し出現頻度をカウント
		for(i=0;i<FileLen;i++)
		{
			ar >> BinData;// 1バイトずつ読み出し
			Counter[BinData]++;
		}

		// ビューにドキュメント更新を伝える
		UpdateAllViews(NULL);
	}
}

/////////////////////////////////////////////////////////////////////////////

 
上記のように単純なif-elseで書き込み用と読み込みように分かれている。

CArchiveクラスの代表的な関数は以下の通り。
Close書き込まれていないデータをフラッシュし、CFileから切り離す
Flush書き込まれていないデータをフラッシュする
operator <<ファイルからデータを読み込む
operator >>ファイルにデータを書き込む
Readファイルから未加工のバイト列を読み込む
Writeファイルに未加工のバイト列を書き込む
WriteStringファイルに1行の文字列を書き込む
ReadStringファイルから1行の文字列を読み込む
GetFileファイルに結び付けられたCFileオブジェクトのポインタを取得する
IsLoadingファイルが読み出し用ならTRUE
IsStoringファイルが書き込み用ならTRUE

またCFileクラスの主要メンバ関数は以下の通り
GetLengthファイルの長さ(バイト単位)を取得する
GetStatusファイルのステータス(作成日時や属性など)を取得する
GetFileNameファイルのファイル名を取得する
GetFileTitleファイルのタイトル(ファイル名から拡張子を除いた部分)を取得する
GetFilePathファイルのフルパスを取得する



(手順5)
CMyHistクラスのOnDrawを編集して描画部分を作る。

/////////////////////////////////////////////////////////////////////////////
// CMyHistView クラスの描画

void CMyHistView::OnDraw(CDC* pDC)
{
	CMyHistDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: この場所にネイティブ データ用の描画コードを追加します。
	if(pDoc->FileName != "")
	{
		int i;
		CPoint pt;

		// ファイル名を表示
		pDC->TextOut(0,0,pDoc->FileName);

		// ヒストグラム(棒グラフ)を表示
		for(i=0;i<256;i++)
		{
			pt.x=0;
			pt.y=i+18;
			pDC->MoveTo(pt);
			pt.x=pDoc->Counter[i];
			pDC->LineTo(pt);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////

 

(手順6)
ビルドして実行。
こんな感じになればOK。
※保存する際のファイル名はデフォルトで開いたファイル名なので、うっかりすると元のファイルの上に保存してしまうので、注意すること!(例題としてちょっと変だな、これ。)







参考書物:
山本信雄 著「プログラマ養成入門講座 VisualC++ 3 はじめてのMFCプログラミング」(翔泳社)
コメント (17) |  トラックバック (3) | 

MFC覚書1:時計表示アプリ

2004年09月22日 00時34分47秒 | VC++の勉強
最近MFCの勉強をコツコツしている。

勉強といっても行き帰りの電車内で本を読む程度なので、頭で理解はしてもナカナカ身に付かない。

MFCはほとんど流儀というか作法というか、ある程度決まりきった書き方があるので、これはもう暗記するくらいの勢いでないと、実践で使えるレベルには到底ならないと感じている。

ということで、今読んでいる翔泳社の「プログラマ養成入門講座 VisualC++ 3 はじめてのMFCプログラミング」の中の例題を自分流に簡略化したものを、このBLOG内にちょっとづつ書きしたためておき、いつでもパッと参照できるようにしておこうと思う。

もちろん「自分のための覚書」なので、間違いがあっても責任はとりません。こんなページを読んでくれている奇特な人はあんまりいないと思うけど、念のため、あしからず。

手始めに一番簡単な時計表示アプリから。


【課題1】時計表示アプリの作成

クライアント領域に現在時刻を表示するプログラムを作る。

(手順1)
MFC AppWizard(exe)でプロジェクト名を「myClock」とする。
ステップ1/6でSDIを選択。あとはデフォルトのままでOK。プロジェクトファイル一式を生成する。

(手順2)
CMyClockViewクラスのOnDrawに追記をする。

/////////////////////////////////////////////////////////////////////////////
// CMyClockView クラスの描画

void CMyClockView::OnDraw(CDC* pDC)
{
	CMyClockDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: この場所にネイティブ データ用の描画コードを追加します。
	CFont *oldFont,newFont;
	CTime ct;
	CString cs;

	// フォントの設定
	newFont.CreatePointFont(300,"MS Pゴシック");
	oldFont=pDC->SelectObject(&newFont);

	// 現在時刻の取得
	ct=ct.GetCurrentTime();
	cs=ct.Format("%H:%M:%S");

	// 現在時刻を表示
	pDC->TextOut(0,0,cs);

	// フォントを元に戻す
	pDC->SelectObject(oldFont);
	newFont.DeleteObject();
}
/////////////////////////////////////////////////////////////////////////////

 
このままビルドするとリアルタイムに更新されない。
(ウインドウの大きさを変えるなどして再描画されると更新される)
よってWM_PAINTメッセージが送られるようにする必要がある。


(手順3)
ClassViewのペインの中のViewに該当するクラス(ここではCmyClockView)上で右クリックし、
「Windowsメッセージハンドラの追加」をクリック。
「ハンドルするクラスまたはオブジェクト」はそのまま(CmyClockView)で、「クラスで使用可能なメッセージフィルタ」はウインドウを選択。
左の「Windowsメッセージ/イベントの新規作成」からはWM_TIMERを選択してOKをクリックする。
するとOnTimer(UINT nIDEvent)という関数の雛形が生成させるので、それに肉付けをする。

/////////////////////////////////////////////////////////////////////////////
// CMyClockView クラスのメッセージ ハンドラ

void CMyClockView::OnTimer(UINT nIDEvent) 
{
	// TODO: この位置にメッセージ ハンドラ用のコードを追加するか
	//またはデフォルトの処理を呼び出してください
	if(nIDEvent == 123)// 123とはここではタイマーの識別子なので適当でOK。
	{
		InvalidateRect(NULL);
	}
	// 基本クラスのメッセージハンドラ(デフォルトウインドウプロシージャ)は
	// そのままにしておくこと
	CView::OnTimer(nIDEvent);
}
/////////////////////////////////////////////////////////////////////////////

 

(手順4)
メッセージハンドラの準備はOKなので、あとはOnDrawの中でタイマーをセットする記述を追加する。
その際、タイマー識別子はOnTimerの中で事前に決めておいた「123」を使うこと。

/////////////////////////////////////////////////////////////////////////////
// CMyClockView クラスの描画

void CMyClockView::OnDraw(CDC* pDC)
{
	CMyClockDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: この場所にネイティブ データ用の描画コードを追加します。
	CFont *oldFont,newFont;
	CTime ct;
	CString cs;

	// フォントの設定
	newFont.CreatePointFont(300,"MS Pゴシック");
	oldFont=pDC->SelectObject(&newFont);

	// 現在時刻の取得
	ct=ct.GetCurrentTime();
	cs=ct.Format("%H:%M:%S");

	// 現在時刻を表示
	pDC->TextOut(0,0,cs);

	// フォントを元に戻す
	pDC->SelectObject(oldFont);
	newFont.DeleteObject();

	// タイマーをセット
	static BOOL fTimer=FALSE;
	if(fTimer == FALSE)
	// プログラムの起動中に1回だけ実行されるようにフラグを立てている
	{
		SetTimer(123,500,NULL);// SetTimerはCWndクラスのメンバ関数
		fTimer=TRUE;
	}
}
/////////////////////////////////////////////////////////////////////////////

 

(手順5)
ビルドする。
以下のようにして時計として動けば成功!




参考書物:
山本信雄 著「プログラマ養成入門講座 VisualC++ 3 はじめてのMFCプログラミング」(翔泳社)
コメント (1) |  トラックバック () | 

一括置換モノも既にあったし。

2004年09月07日 02時11分13秒 | Perl関連
=自分自身への釘刺しメモ=
まちがってもこんなの自作しないように。

一括置換のスクリプト書くならコレ使うべし。
File::Searcher


コメント (0) |  トラックバック () | 

File::Find::Ruleの中で発見。

2004年09月07日 01時36分50秒 | Perl関連
ちょっと前に「find して grep !」という日記を書いたが、
その後そのネタをモジュールとして書いてみようと色々試していたが、速度の面でイマイチ納得がいかなかったり、モジュール化したときの相対パスの指定とか、ちょっとした壁にぶち当ってました。

で、なんとなくもう一度CPANを調べていたら、なんと既に考えていたモノはありました。アホクサ。。。

File::Find::Ruleという名前で、そのメソッドの中にしっかりgrepってのがありました。

File::FindとFile::Grepを組みあせるのは「妙案」かと思ったが、その目的だけでモジュール化ってところが些かナンセンスかなぁ、って感じ始めてたんだよね。。。

でもまぁ気を取り直して、一応このモジュールの使い方を調べたので、もったいないから覚書き。


基本的にはperlOO的に書く方向を採用するとして、、、


【基本形】
use File::Find::Rule;
# オブジェクト生成
my $rule = new File::Find::Rule;
# fileかdirectoryのどっちかを最初に決める
$rule->file;
# nameの意味はそのまんま。中身は正規表現でももちろん可。
$rule->name( '*.pm' );
# inかstartでクエリー実行。
my @files = $rule->in( @INC );
# シンプルにファイル名を出力。
for(@files){
 print "$_n";
}

 

要するにfileを探したいかdirectoryを探したいか決めて、条件も指定して、クエリー実行。で取得した配列にファイル名がはいってるよ、というものらしい。

クエリー実行のメソッドにはstartというのもあるが、これは直接は結果が返らないので、matchというメソッドで
while ( my $file = $rule->match ) { .....
のように書くらしい。無駄な配列変数にコピーする必要がないからメモリ効率はすこしいいのかな??良くわからないけど。

次はgrepメソッドを使ったパターン。

【grepと組み合わせ型】
use File::Find::Rule;
my $rule = new File::Find::Rule;
$rule->file;
$rule->name( '*.pm' );
# qrは正規表現を意味しているらしい(知らなかった)
$rule->grep(qr/use strict/);
my @files = $rule->in( @INC );
for(@files){
 print "$_n";
}

 

うん。普通だ。とっても。

でトリッキーな使いかとして否定形のものもサンプルとして上がってた。

【grepと組み合わせ型−否定形−】
use File::Find::Rule;
my $rule = new File::Find::Rule;
$rule->file;
$rule->name( '*.pm' );
# shebangで始まるperlのファイルを無視する振る舞い
# negativeな名無しサブルーチンを無名配列で挟むらしい。
$rule->grep( qr/^#!.*bperl/, [ sub { 1 } ] );
my @files = $rule->in( @INC );
for(@files){
 print "$_n";
}

 

この使い方は意味不明。
CPANの英文読んでてもさっぱり意味が伝わってこない。
negativeって言ってるのになんでsub{1}なの?

わからんことだらけなので、実際に試してみました。
ああ、なんとなく言いたいことがわかってきたかも。。。
どうやら1行目で#!..perlとなっているものだけをピックアップするもの、つまり2行目以降とかPODの中でのマッチを否定する、という意味合いになるらしい。

それにしても、あの英文では理解できんなぁ。



で、実際に使ってみた体感速度だけど、結構遅い。
マシンが腐ってるせいもあるけど、やっぱり結構待たされるなぁ。
Unixのfindコマンドで
find / -name ".perl" -exec grep "use strict" {} ;
と叩いたものにははるかに及ばないよ。まぁ当たり前と言えば当たり前だけど。

コレだったら自作したモジュールとも速度かわらんなぁ。
実行結果がファイル名を返すだけ、っていうのもシンプルすぎるし。

とケチをつけてみたものの、やっぱり自作するよりコレ使った方が賢い。
「車輪の再発明」を"意図せず"やってしまうのは、相当むなしいからな。


っていうか、ただの時間の無駄!!
コメント (0) |  トラックバック () | 

grepについて調べたら

2004年08月29日 00時43分09秒 | Perl関連
unixコマンドのgrepとperlのgrepは微妙に違いがある。

ちょっと比較してみよう。

UNIXコマンドリファレンスより=
grep ファイル内の文字列を検索する
書式 grep [オプション] [文字列パターン] [ファイルn]
オプション
-v 指定した文字列パターンを含まない行を表示する
-n 行番号を付けて表示する
-l 指定した文字列を含むファイル名を表示する


日本語perl texinfo - grepより=
`grep(EXPR,LIST)'
LIST の全要素について EXPR を評価し (各要素を $_ にセットしながら)、 expression が true であると評価された要素からなる配列を返す。スカラーのコンテキストでは、expression が true となった回数を返す。
@foo = grep(!/^#/, @bar); # コメントを無視する
注意: $_ は配列値への参照となっているので、配列要素を変更するのに用いることができる。これは便利である一方、 LIST が名前のついた配列でない場合には、ちょっと変な結果を返すことがある。


違いがるのはperlの第2引数が「LIST」であるってところかな。
普通のUNIXコマンドだと第2引数はファイル名を渡すけど、perlの場合ファイル名ではなく、あくまでも「LIST」なんだよね。


だからその都度

open(FILE,'hoge.txt');
@return=grep /fuga/ <FILE>;

 
とか書かなければいけない。不便じゃんか。なんとなく。


でも圧倒的にイケてる部分もある。
実は第1引数はEXPRだけでなくBLOCKでも良いのだ。

Perl's map, grep and sort functions - RayCosoftより=
The grep function
(If you are new to Perl, skip the next two paragraphs and proceed to the "Grep vs. loops" example below. Hang loose, you'll pick it up as you go along.)

grep BLOCK LIST
grep EXPR, LIST

The grep function evaluates the BLOCK or EXPR for each element of LIST, locally setting the $_ variable equal to each element. BLOCK is one or more Perl statements delimited by curly brackets. LIST is an ordered set of values. EXPR is one or more variables, operators, literals, functions, or subroutine calls. Grep returns a list of those elements for which the EXPR or BLOCK evaluates to TRUE. If there are multiple statements in the BLOCK, the last statement determines whether the BLOCK evaluates to TRUE or FALSE. LIST can be a list or an array. In a scalar context, grep returns the number of times the expression was TRUE.


だから正規表現だけでなくBLOCKとして式が渡せるってことで

@unique = grep { ++$count{$_} <2 } 
 
こんな書き方もできてしまうわけで、その意味ではかなりイケてます。


ふーん、って感じのことだけど。
ちょっと勉強になりました。
コメント (0) |  トラックバック () | 

find して grep !

2004年08月28日 23時57分53秒 | Perl関連
Windowsで作業しているとき、あるディレクトリから下部の特定のファイルの中で、何かしらのキーワードを含むものだけを抽出したい、なんてことが今までに何回かあった。

▼unixコマンドだったらこう
find / -name "*html" -exec grep -l 'test' {} ;

たったこれだけのことなんだけどね。
cygwinとか入れてない限りwindowsではちょっとつらいところだ。

都度perlでコードを書いていくのちょっとダルそうなので、なんかモジュールになって転がってないかなーと思いCPANで探したのだが、どうやら"そのものずばり!"ってモノはないらしい。

File::Grepっていうモジュールがあったので、これとFile::Findを組み合わせればなんかできそうだぞ。

この際だからモジュール化しておこう!
さっそくFile::Grepを落としてきて実験。

ん?なんかだめだぞ。なんでだ???

と大分苦しんだのですが、File::Grepのfgrep関数に渡す第2引数はリストを渡すようになってるんだ
けど、これがsortされてないと駄目みたいなんだな。例ではglob関数を使ってカレントディレクトリのリストを渡していたんだけど、glob関数の結果ってきれいにsortされた状態になってんだね。ってそんなこと何処にも書いてないから気付かなかったよ!えらい時間を費やしてしまったさ!

気を取り直して再度トライ!
OK!概ね予想通りに動いたよー。

まだ荒削りなので、もっとブラッシュアップしたら公開しようかなー。

コメント (0) |  トラックバック () | 

夏の風物詩 2

2004年08月28日 22時47分27秒 | 季節モノと生活全般
カミサンが自分をびっくりさせるため(?)に作った「顔チョウチン」。

家に帰ってきたらドア開けた瞬間にこれがぶら下げてあって、びっくりした。

なんとなつ夏っぽかったので投稿ー。
コメント (0) |  トラックバック (1) | 

夏の風物詩

2004年08月28日 22時06分50秒 | 季節モノと生活全般
数日前だけど、夏の終わりにようやく夏らしいことをした。

家の前での花火だ。
子供と嫁さんと3人で、かわいらしい花火大会をした。

なんかいいもんだね。こういうのって。
コメント (0) |  トラックバック () | 

ベイズ理論で脱スパム!

2004年08月25日 22時29分32秒 | ネットで見つけたモノ
自宅のPCで使っていたメールアドレスが随分前から「スパム受信用専用アドレス」とでも表現したくなるような、ひどい状態だった。

多い時は日に200〜300通届く。主にバイアグラとか出会い系とか、国内外問わず、とにかく色々と送られてきて、そのメールアドレスはもう諦めていた。

ところが、数日前にBulknewsの過去記事POPFileというperlで書かれたベイジアン・スパムフィルターのことを知った。

興味を持ったのでベイズ理論のことをちょっと調べてみた。

結構お熱い話題だったらしく、「Niftyもベイジアンフィルターを搭載したメールサービスをはじめた」とか、「Googleの検索アルゴリズムにも応用されている」とか、数多くのブロガーたちにより何だかんだと注目&論評されている。

ちょっとつらつらと考えたのだが、この手の理論の応用モノは意外とネットビジネス系の人たち(主に技術者系)に結構好まれるネタだよな、と。

かくいう自分も1年ぐらい前にはマルコフ連鎖による人工無能づくりに燃えていたりした。

コンピュータが自律的に情報を記憶し学習し、期待以上の判断をしてくれる。人間はそれを見て楽しんだり、または便利に使ってみたりする。

ちょっとしたエージェント型のサービスなんかが、自分のアイデア次第で開発できるんじゃないか、とそんな気にさせてくれる「可能性」のようなものがあるんだよね。

そこのところがネット系の仕事に従事している人の好奇心をくすぐるんだろうな、と思います。

で実際にこのPOPFileを使ってみて、たった1日しかたってないのに、この時点で驚くほどの高い確率でスパムを適切に判断してくれました。

単なる「理論による可能性」を「現実の価値」に変えている人たちが、世界中にたくさんいるんだなぁ。

しかもperlでWindowsクライアントとして提供しているんだもんな。

自分が考え付く程度のことなんて、世の中のどっかで誰かがもっと先を考えている、そんなことを改めて痛感しましたですー。
コメント (0) |  トラックバック () | 

2004年08月23日 15時00分18秒 | ネットで見つけたモノ
うちの女房は蚊が嫌い。
「プィーン」と聞こえたら、叩き潰すまで半狂乱。

たしかに虫刺されに弱い人や異常に痒がりの人からしたら、まさに悪魔だよね。でも蚊ってヤツはなかなかし止められないんですよ。

だから夜中なんか安心して眠れないわけです。

なんとかしなければ、と思い「蚊」で検索したら、なんとも目を引く一品が・・・

その名も「デジタル蚊取り線香」。

パソコンの内臓スピーカーから蚊の嫌いな周波数の音が数秒おきに「ピー」って出るんだって。

「あほくさ」と思ったけど、とりあえずダウンロード。試してみました。

確かにピーピー鳴ってるよ(笑)。

会社で正面に座ってる人は「なにこの音?」って顔してます。かすかに聞こえる程度の音だけど、気になるって言えば気なるような音だね。

タスクトレイに常駐するときのアイコンはなかなかイイです。
あと「殺虫スプレーモード」ってのもクダラナくてグッドです。

それにしても実際に効果あるんだろうか?
さっそく今晩から試してみよう。





コメント (1) |  トラックバック () |