2004年6月のアレ
2004年6月30日
Web Designing、2004年7月号を買って、 PHPのソースで気になった所を。
$d = dir("./data/"); while (false !== ($entry = $d->read())) { if(eregi($key,$entry)){ $l=file("./data/$entry"); $lines[$cnt]=$l[0]; $cnt++; } }
全体に、空白の使い方に統一性がない。 このために、readability が低下している。 空白が視覚的な認知性を左右する、 というような認知心理学の基本は、 この雑誌の著者に対してはあえて述べるまでもないはずだと思うのだが、 不思議だ。 Webページとプログラムのソースコードは別だということなのかもしれない?
"./data/" というリテラルが2箇所に出てくるのはよくない。
while の条件判定部分の書き方、 これは PHP ではよく見るのだが、 PHP はあまり経験ないので分からないのだが、 このように書く理由は何かあるのだろうか? 「false != 何々」ではなく「何々 != false」と書くのが 人間の認識パターンとしては自然なのでバグの原因となることを避けられる、 ということはかなり昔のコラムで書いた通りである。
修正したコードの例は次の通り。
$basedir = "./data/"; $d = dir($basedir); while (($entry = $d->read()) !== false) { if (eregi($key, $entry)){ $l = file("$basedir$entry"); $lines[$cnt++] = $l[0]; } }
*
function cmp ($a, $b) { if ($a == $b) return 0; return ($a > $b) ? -1 : 1; }
PHP アレなんで済みませんが、ここは === でなくて == でいいのだろうか、 という疑問がふと沸いた。 PHP に詳しい人に聞いてみたい。 その件は置いといて、 プログラミング的には次のようにした方がいい。
function cmp($a, $b) { if ($a > $b) return -1; return ($a != $b) ? 1 : 0; }
元のアルゴリズムは、まず一致しているかどうかを確認する。 変更後のアルゴリズムは、まず$aが$bより大きいかどうかを確認する。 データの性質にも依存するが、 ソートのための比較関数が呼ばれるときの状況としては、 特別な条件がなければ、 比較するデータが一致するよりも、一致していない場合が多いと考えてよい。 今回もそれに該当すると思われる。 つまり、殆ど成功しない「等しいか」という比較演算を毎回行うのは、 処理としては無駄が多いのである。 ハードウェアの処理能力がいくら向上しても、 こういう些細な無駄の積み重ねは、結果的にサーバーの負荷という形で全体に影響を与えることも危惧して損はないと思う。
foreach($lines as $l){ //年齢層別にカウント $op=explode(",",$l); $key=$op[7]; //年齢 if($key<=20){$age[0]++;} if($key>20 && $key<=30){$age[1]++;} if($key>30 && $key<=40){$age[2]++;} if($key>40 && $key<=50){$age[3]++;} if($key>50){$age[4]++;} } $label=array("~20","21~30","31~40","41~50","51~");
例えば $key が 19 である場合の処理の流れを考えてみると、 アルゴリズムとして、どこが無駄なのか分かる。 それが readability に貢献しているのならいいのだが、 この場合は、貢献よりむしろ低下させていることが問題である。
また、ここで5種類の場合に分けていることが、 後で出てくる for ループの中に出現しているマジックナンバー「5」に対応してしまっているのは、 今後の修正の時のバグの原因になるのが大問題である。 また、場合分けを修正したときに、 $label の内容を同時に修正し忘れると、 ヘンな画面を表示してしまうことになる。 これも問題である。 ただ、それらを解決するのは結構面倒な話だ。
$values = array(20, 30, 40, 50); $size = count($values); // ラベルを動的に作る // 最初と最後はループの外で作っている $label[0] = "~$values[0]"; for ($i = 1; $i < $size; $i++) { $from = $values[$i - 1] + 1; $label[$i] = "$from~$values[$i]"; } $label[$i] = "$values[$i - 1]~"; foreach ($lines as $l) { //年齢層別にカウント $op = explode(",", $l); $key = $op[7]; //年齢 if ($key <= $values[0]) { $age[0]++; } elseif ($key <= $values[1]) { $age[1]++; } elseif ($key <= $values[2]) { $age[2]++; } elseif ($key <= $values[3]) { $age[3]++; } else { $age[4]++; } }
ラベルを作る処理は別関数にした方がよさそうである。 if ~ elseif の所は、 あえて列挙してあるのだが、 ループで処理できなくもない。
for ($i = 0; $i < $size; $i++) { if ($key <= $values[$i]) { $age[$i]++; break; } if ($i == $size) { $age[$i]++; } }
$values に番兵を置くと、ループが終わった後の処理を省略できる。
$values = array(20, 30, 40, 50, 999); $size = count($values) - 1; $sizeplus = $size + 1; // ラベルを動的に作る // 最初と最後はループの外で作っている $label[0] = "~$values[0]"; for ($i = 1; $i < $size; $i++) { $from = $values[$i - 1] + 1; $label[$i] = "$from~$values[$i]"; } $label[$i] = "$values[$i - 1]~"; foreach ($lines as $l) { $op = explode(",", $l); $key = $op[7]; //年齢 for ($i = 0; $i < $sizeplus; $i++) { if ($key <= $values[$i]) { $age[$i]++; } } }
何かイヤな感じに技巧的すぎる。 さらに検討できる部分が多いというべきか。
ちなみに、 「余計に見にくくなっているのでは」という批判はあえて否定しないが、 マジックナンバー(ここでは、分類の数)が除去できている分、 そう捨てたものでもないと思うし、 根本的に性格の違う処理になっていることを強調しておきたい。 この性格の変更というのが、 例えば、 「グラフに51~60歳を追加してね」とか、 「5歳刻みにしてくれ」と言われたときに威力を発揮するわけだ。
蛇足ですが、もちろん、
$values = array(20, 30, 40, 50);
という所を、
$values = array(20, 30, 40, 50, 60);
とか、
$values = array(20, 25, 30, 35, 40, 45, 50);
と直すだけでよいのである。 但し、 そのためにはこの修正例には出てこない別の箇所も手を入れて、 マジックナンバーを除去気しておく必要がある。
2004年6月24日
背景色がアレで文字が見えないサイトが非常に多く存在するのだが、 デザインの分野というか、 特にTVの画面なんかだと、 背景が五月蝿い時には白抜き文字を使うというのが常識になっていたと思う。 しかし、白抜き文字を使っているサイトはあまり見た記憶がない。 フォントが指定できないから?
2004年6月24日
スケジュールを予約しておいて、 その事前にメールで通知する処理の話。 何月何日何時という指定なら、 その10分前にメール、という感じで済むのだが、 これを毎週金曜日の22:00、ということで通知するにはどうするか?
2004年6月22日
MySQL の日本語リファレンスマニュアル(多分英語も?)なのだが、 背景がやけに暗くて読めなかったのだが、 電車の中でテーブルの構造を変更しようとして、 マニュアルを見ようとしたのだが、 さらに読めない度がup。 バッテリー利用時にはバックライトが暗くなるためだが、 このマニュアル、皆さんは平気で読んでいるのだろうか?
ドキュメントの先頭を見ると、 BGCOLOR が silver と指定されている。 これを white に変更して、読めるようになった。 何か深い意味があって silver にしているのだと思うが、 理由を知りたいものだ。 とりあえず、個人的には読めないとマニュアルの意味がないので、 white に変更して使うことにする。
§
Xoops でスケジューラを作ろうとしたのだが、 スケジュールの通知の所でハマリ中。 例えば6月30日12:00に会議、のようなスケジュールを今入れた場合、 (1) 今、 (2) 6/30まで毎日、 (3) 6/30の12:00直前、 の3パターンに対して、指定したアドレスにメールを送信したい。 この場合、 (1)と(2)はpc、 (3)は携帯にメールを入れたいと。
このようなスケジュールをDBに登録するのは簡単なのだが、 Xoops というのは基本的に Web アプリなので、 定期的に何か実行するデーモンのような使い方は想定されていないと思われる。 私が知らないだけかもしれないが、 指定した時間に何かする、なんてAPIはなさそうだし。
ということで、どうするか。 案1。 Xoops にキックアップの処理を作る。 キックアップって言うのか? どこか Xoops の URL をアクセスしたら、 通知のメールを送信する、というような使い方をする。 実際にアクセスするのは cron を使う。 Xoops のスケジューラで cron に蹴りを入れる時刻を登録し、 その時刻になれば、cron が Xoops を呼び出す。 問題はユーザー認証というか、セキュリティ的なところか。
案2。 スケジュールの通知処理は Xoops とは全く別に用意する。 Java で作れば簡単かな。 そうなると、 スケジューラーも Java で servlet にすれば話が早いのだが、 Xoops の練習にはならないのでナニですけど。 Java 側を XML-RPC で受けるみたいな技はあるかもしれない。 この場合の問題は、 DB をどう共有するか、あるいはしないか、というあたり。 これも、Java だけで daemon 的に走らせるか、 指定時間ごとに cron がトリガーかけるか、 という話はある。
DBを共有しないとすると、 通知DBのテーブルとしては、 通知ID、スケジュールID、通知時刻、通知先アドレス、通知文、 の5つを指定するようにして、 (1) 新規追加、 (2) 指定スケジュールIDに対するレコードの削除、 ができるようにしておけばいい。 Xoops 側で時刻、通知文などの変更がある場合は、 一旦全部削除してから新規追加、 ということにする。 安直ですが、こういうテーブル作ればいいか。
id int unsigned NOT NULL auto_increment, scedule_id int unsigned NOT NULL, time timestamp NOT NULL, email text, body text
2004年6月14日
ココログに書こうと思ったら緊急メンテナンスとかいう。 書けなかった。
そういえば最近妙に重いというか、 レスポンスだめぽじゃないですか、気にしないけど。
§
http://www.psd.co.jp/
§
AC電源まわりを組みなおして、 VAIO Z がないと使わないのをスイッチ付きのテーブルタップに集約。 集約って言葉の使い方、これでよかったっけ?
§
新生銀行のサイトにログインしようとしたら例のポップアップが出ないという話。
2004年6月11日
PicoShot で撮影していたら声をかけられた。 いや、ターイホされた訳ではないですよ…。
どうも珍しかったというか、興味があって「それは何だ」ということらしいので、 多少説明してみた。
2004年6月3日
Xoops のモジュールをテキトーに作ってみた。 まず、modules の下にディレクトリを作る。 こんな感じ。
cd modules mkdir sample chmod 755 sample
contact という標準モジュールをコピって修正してみることにした。
cd contact cp -pR * ../sample cd ../sample
なーんと、この後の記憶がない。
2004年6月1日
Cマガジンの記事を送る。 〆切は5/31なので1日オーバーした。
Forrest はバージョンアップしないのだろうか、 とか思って公式サイトを見たが、 特に変わった様子はない。