タイトル通りのものをGetTitle.pyとして公開してみます。このサイトで使っているままのもので、NewsClipではこのファイルそのものをimportしてタイトル文字列を割り出しています。まだ不完全であることは間違いなく、恐らくメンテナンスされてゆくことでしょう。 引数がファイルオブジェクトではなくてファイル名というところあたりはちょっと良くないよなーなどとは思っております。 あと、毎日新聞を完璧にサポートすることがかなり難しい。元データはNewsMLだと思うんだけど、HTMLにするときのXSLT(なの?)がいろいろあるらしくて、HTMLから記事のタイトルを引っ張ってくるパターンがうまく作れない。悩ましい。
Category: プログラミング
Pythonでカレントディレクトリにある*.pyをimportすると、いつの間にかコンパイルされた*.pycが出現していたことに気付いて驚愕。 Pythonよ、それを勝手にやっちゃうのか…これはよくできていると言うべきか、やりすぎと言うべきか。しかも元の*.pyよりもかなりデカくなるねえ。 これはたぶんタイムスタンプかなんかを見て*.pyを使って*.pycを作るか*.pycを使うかを自動的に判断するのだろう。しかしタイムスタンプがアテにならない環境を使っている場合はどうしろと言うのか(そんな環境で作業する奴が悪い!)。
C/C++のソースを印刷するのにc2ps(technion.ac.il)を使いたかったのだが、日本語が化けるのでしょうがなくa2psを使っていた。 しかし、-Xオプションで日本語が出る(uec.ac.jp)というではないか。騙されたぜ。…けっこう長いこと騙されてたよ。 (追記) 2004-03-04 13:13 RPMにするとき用のSPECファイルを置いとく。
PHPで性能を無視して書いたプログラムをどうにか高速化しようというのがあって(切実な問題?)、いろいろ調べてみようと思い立った男がいたとして下さい。やっていることは、文字列処理が多いと思う。どう書いたら速くなるのか。 いろいろ調べようと思っているが、まずは少しやってみてわかったことを書く。 文字列の連結についてはいくつかの方法がある。ぱっと思いつくものでも以下の4つである。
- .=演算子(
$var.=val
) - .演算子(
$var=$var.val
) - ““の展開(
$var="{$var}val"
) - sprintf(
$var=sprintf("%sval", $var)
) - join(
$var=join("", array($var, val))
)
このうち、1.の.=が一番速いということがわかった。試行を10万回繰り返して平均を取ると、一回の演算にかかるコストは「$var.=val
」が約2.6us、「$var=$var.val
」は約40us、「$var="{$var}val"
」が約42us、「$var=join("", array($var, val))
」が約44us、「$var=sprintf("%sval", $var)
」が約82usだった。つまり「.=」が圧倒的に速い。sprintfが遅いのは当然としても、それ以外のやつも全部オブジェクトを生成してしまうような感じだ。「.」でつなげるのと「""」で展開していくのはほとんどコストが変わらない。ただし高速化したいのなら「.=」をいくつも書いてつなげたほうがよいということがわかる。
では文字列はどう表現したら速いのか。「"(ダブルクオート)」よりも「’(シングルクオート)」が高速なのは想像できるが、変数に入れて使い回すと速くなったりするのだろうか?
結論を言うと、“と’の差は3〜4%程度までである(“は何も展開されない場合)。10万回のループを何度か試行してみると、ときどき"のほうが’よりも若干高速になることすらあるので、“と’の違いは誤差の範囲であろう。しかし、変数に文字列を入れて参照すると、10%ほど遅くなる。これは常にそうだった。
$a=“test”;
$b=$a;
$c=$a;
よりも
$a=“test”;
$b=”{$a}”;
$c=”{$a}”;
は40〜50%遅くなる。まあこれはいいだろう。しかし、
$a=“test”;
$b=“test”;
$c=“test”;
にすると最初のものよりも10%高速になるのである。ただしこれは1回の演算につき1us程度の部分なので、最適化という点でこの10%を利用するのは無理があると思う。変数を手動で展開すれば1回走るごとに約0.1us速くなる、と覚えよう。ループの中なら意外と変わるかもしれない。展開してメンテナンスがやりにくくなるよりは最初の、文字列の連結の実験でわかったように、生成されるオブジェクトの数を減らすように書き方を工夫するのがよいだろう。
こういう傾向はPHPのバージョンによるのかもしれない。みなさんも実験してみてほしい。私が試しているバージョンはLinux(Kernel 2.4.x)のPHP-4.2.2(Red Hat Linux 8.0のphp-4.2.2-8.0.8)である。
テストプログラムはtest2.php(文字列連結)、test3.php(文字列代入)に置いておく。test2.php、test3.phpとして保存して、
bash# php test2.php | awk '{a[$1]+=$2;b[$1]++;}END{for(i in a){if(b[i]!=0 & a[i]!=0){print i, a[i],a[i]/b[i],a[i]/b[i]/10000;}}}' | sort -n +1
などのように実行するとよいだろうと思う。
Shift_JISは「\」が入るから(「表」問題)プログラム中では使わないほうがよく、EUC-JPを使うべし、とは思っていたが、なぜかJISコード(ISO-2022-JP)に問題があるとは思っていなかった。まあ無意識に使ってなかったんだけど、ISO-2022-JPって「\」とか「%」入るのな。考えてみれば当然なんだが。 Unicode化gccとかって作ったら偉いのかな? 多言語混合変数名。別にISO-2022-JP化でもSJIS化でもいいけど。
今日はCのプリプロセッサ(cpp)の話。
私はまだ未熟者であるから、多用しつつもcppの使い方を知らずにいた。#define
でエラーコードが定義されているとして、エラーコードの名前と値をくっつけた文字列を得る方法。
#define xstr(s) str(s) #define str(s) #s #define errstr(x) #x"("xstr(x)")"
で、
#define EIO 5 /* I/O error */ : puts(errstr(EIO));
はどうなるか。まさにEIO(5)と表示されるのだ!!
まさに魔王(なんだそりゃ)。この、名前を文字列にする#
は知っていたけど、評価した結果を文字列にするためにワンクッション置くってマクロは知らなかったなぁ。info cppより。
strerror()を使えなんていう野暮を言う奴はいないよね。
(追記) 2004-02-17 15:53
こういうことができてしまう#defineが便利なせいでenumが流行らないのだ(むかむかっ)。エラーコードなんてenum使いたいとこなのに。
サンプルコードは以下の通り。
#include
#include
#define xstr(s) str(s)
#define str(s) #s
#define errstr(x) #x"(“xstr(x)”)"
int main(int argc, char **argv)
{
puts(errstr(EIO));
return 0;
}
どうだ。
私の場合はもっとがんばって、
#define D(x) case x: return #x"("xstr(x)")"; char *errcode2str(int err); switch(err){ D(EPERM); D(ENOENT); D(ESRCH); default: return "Unknown"; } } #undef D
のようにするのが好みだ。そしてソース自体を
imgtag(mozdev.org)というプラグインを使っている。これはコンテキストメニューに「Make IMG Tag」という項目が出てきて、選択するとクリップボードにというタグが入っている、というものだ。これをペーストすればimgのtagがカンタンに書ける。 ところがこれ、imgのwidthやheight属性は入れてくれるけど、altやtitle属性は入れてくれない。ソースを見たら、入れてなかっただけなので直してみたら簡単に直った。 …という話ではない。 Firefoxのプラグイン(いわゆるXULアプリケーションなのかな)は.jarファイルに入っているが、jarというのはJava系の人が使っている、実体はzipファイルそのものである。j2sdkにもjarコマンドというtarに似せたインタフェースのJavaアプリがあるが、やはりできるのはzipファイルそのものだ(MANIFEST.MFという妙なファイルが追加されてしまうが)。ということでimgtag.jarをunzipで解いて1行だけ書き直してzipで戻してみたらそれで動いたのだが、そういうもんでいいのかな?? 正式な作り方を知りたいなぁと少し思ってみた。暇を見つけて調べてみることにしたい。と思ったメモです、この文章は(長い)。 まあついでだし、imgtagへのパッチを置いておこう。パッチって言ってもほんの1行。こういうものは作者に送ったほうがいいのかなとも思うけど、すでにココ(mozdev.org)のところのコメントに書いてあるからいいか。
普通はリンカはライブラリ(.a)になっている場合にはいらないファイル(.o)ごと消してくれる。これを関数単位で消してもらおう、という話。
-ffunction-sections -Wl,--gc-sections
らしい。ここ(radiumsoftware.com)より。
しかし、消えない。Oops!
使っていないfunc2()
が残ってしまう。
#include <stdio.h>
int func1(){ printf(“Hello, World!\n”); }
int func2(){ printf(“Good-bye, World!\n”);}
int main(int argc, char **argv){
func1();
return 0;
}
##
gcc -ffunction-sections -Wl,–gc-sections test.c
##
objdump -d a.out
:(略)
08048338 :
8048338: 55 push %ebp
8048339: 89 e5 mov %esp,%ebp
804833b: 83 ec 08 sub $0x8,%esp
804833e: c7 04 24 e4 83 04 08 movl $0x80483e4,(%esp,1)
8048345: e8 1e ff ff ff call 8048268 <_init+0x38>
804834a: c9 leave
804834b: c3 ret
0804834c :
804834c: 55 push %ebp
804834d: 89 e5 mov %esp,%ebp
804834f: 83 ec 08 sub $0x8,%esp
8048352: c7 04 24 f3 83 04 08 movl $0x80483f3,(%esp,1)
8048359: e8 0a ff ff ff call 8048268 <_init+0x38>
804835e: c9 leave
804835f: c3 ret
:(略)
しかしながら、-ffunction-sections
は機能していて、以下のように関数毎にセクションに分かれてコードも正しく配置されている。
##
gcc -c -ffunction-sections test.c
##
objdump -d test.o
test.o: file format elf32-i386
Disassembly of section .text:
Disassembly of section .text.func1:
00000000 :
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: c7 04 24 00 00 00 00 movl $0x0,(%esp,1)
d: e8 fc ff ff ff call e <func1+0xe>
12: c9 leave
13: c3 ret
Disassembly of section .text.func2:
00000000 :
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: c7 04 24 0f 00 00 00 movl $0xf,(%esp,1)
d: e8 fc ff ff ff call e <func2+0xe>
12: c9 leave
13: c3 ret
Disassembly of section .text.main:
00000000 :
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: 83 e4 f0 and $0xfffffff0,%esp
9: b8 00 00 00 00 mov $0x0,%eax
e: 29 c4 sub %eax,%esp
10: e8 fc ff ff ff call 11 <main+0x11>
15: c9 leave
16: c3 ret
してみると、リンカが悪いのかな?? binutilsのバージョン上げてみようかな。
(追記) 2004-02-03 13:26
-static
つけたら消えた。
よくあるグラフっぽく書くとすると、以下のような感じ。 zsh# R : [Previously saved workspace restored]
ma <- function(a,t){ # array, t から移動平均の値を求める
- r <- array()
- for(i in 1:length(a)){
- r[i]=mean(a[max(1,i-t):i])
- }
- (r)
- }
plot(your_array) # 通常のグラフ lines(ma(your_array, 25), col=“red”) # 25日移動平均線を重ね書き lines(ma(your_array, 75), col=“green”) # 75日移動平均線を重ね書き 次はローソク足のグラフ書きにチャレンジしてみようかな(って別に株価の分析をしたくてRを使おうと思ったわけじゃないのに…)。 図書館で借りてきたSの本には
tsplot()
っていう関数があるように書いてあるんだけど、Rにはないようだ。よくわかんないけど関数が定義されていない。plot()
を使うと重ね書きにならないので、最初にplot()
でグラフを作ってその上にlines()
とかpoints()
を使って重ね書きをするとよいだろう。
時間の話が続くが、Windowsのタイムスタンプ(ファイルの更新時刻など)は1601年元旦から何100nsecかかったか、の64ビットの数値である。Unixは御存知の通り1970年元旦からの秒数(構造体になってusもしくはnsがつくこともある)で、32ビットである。 というわけで、コンバートする必要があるのだが、閏年がたくさんあったりして、正確に求めるのが面倒だ。Pythonのコードでメモしておく。11644473600秒というのがマジックナンバー。1601年元旦から1970年元旦までには134774日の差がある。
# Windowsのファイル時刻 -> Unix time # Windows: EPOCH = 1601-01-01 00:00.00 GMT から n*100nsec # Unix : EPOCH = 1970-01-01 00:00.00 GMT から n*sec # returns: (sec, usec) # 不完全 def win2unixtime(t): timediff=(int)((1970-1601)*365.2425)*24*60*60 # 11644473600 us=(t%10000000)/10 sec1=t/10000000 # WinEpochからの秒数 sec=sec1-timediff return (sec,us)
暦としては、現在の暦では4年に1度の閏年、100年に一度の閏年回避、400年に一度の閏年回避の回避ということで、365+1/4-1/100+1/400 = 365.2425日ということになる。 「11644473600」でぐぐるといろいろ出てきます。