Skip to main content

ログ取得ツール

Category: プログラミング

比較

バージョン番号の比較(というかソート)ってどうやってます? ちょっと作ってみたけど…完全ではないなぁ。 これだと文字数で0を足しているだけなので、整数だけとかアルファベットだけならいいけど「9a」(9にパッチが当たった想定)と「10」で、「9a」が後になってしまう。jpegのライブラリとかこういうスタイルだったよな確か。 例えばLinuxみたいに2.6.8-bk1と2.6.8-rc1があってrc1のほうが後だってのはプログラム上はどうするべきなんだろう。Firefoxみたいにrcとprの違いとか。あと、例えば「rc1a1」と「1rc2」みたいなあまり互換性がなさそうなつけ方があったとして、どっちが後とするべきなんだろう。rpmのソースとか見てみようかな。

#! /usr/bin/python

import string

def fix_length(x,y):
    m=max(len(x), len(y))
    x.extend("0"*(m-len(x)))
    y.extend("0"*(m-len(y)))
    return x,y

def fix_length_str(x,y):
    m=max(len(x), len(y))
    return x.zfill(m), y.zfill(m)

# "n.n.n-n.n.n"
def compare_version(x,y,spl=''):
    # split "-"
    # print "cmp0", x,y
    if len(spl)==0:
        # return cmp(string.atoi(x),string.atoi(y))
        x,y=fix_length_str(x,y)
        return cmp(x,y)
    x1, y1=x.split(spl[0]), y.split(spl[0])
    x1, y1=fix_length(x1, y1)
    for i in range(len(x1)):
        r=compare_version(x1[i], y1[i], spl[1:])
        if r!=0:
            return r
    return 0

if __name__=='__main__':
    a=["1.0.0", "0.90", "0.1", "0.9", "0.10", "1.2.5", "1.2.5-10", "1.2-20", "1.2.5-9"]
    a.sort(lambda x,y: compare_version(x,y, "-."))
    print a

結果は以下。

タコの足は8本! イカは10本!! (じゃあムカデは?)

つるかめ算の話ではない。8進数の話。 C言語の設計ミスの大きな部分を占めているのは「0ではじまる整数を8進数とみなす」ところにあると思う。それでいて0.1は10進数の浮動小数点数なのだ。0ではじまれば8進数、という単純な話じゃなくて、整数という呪いがかかっている。しかもatoi()一発では取れないのが情けないという。うんざりだ。そういうわけで、例えばC言語のプログラマは「09:00起床」というメモを書けなくなってしまう。09はダメだよ。あっという間にエラーだ。8進数に9て。いい子は9時に起きたきゃ「011:00起床」と書きなさい(おいおい)。 従順なプログラマの誰もが反攻の狼煙を上げないまま、世界がそういう常識になってしまった。 今から作るとしたら、何もなければ普通に10進数で、0bではじまれば2進数、0xではじまれば16進数、0oではじまれば8進数、0dではじまれば10進数、0a(n)ではじまればn進数、浮動小数点数も同じ扱い、とするのが正しいと思う。つまり、16進数で256と書こうとした例だと、、、

unsignedの話の続き

ひとつ前の記事で、unsignedを追放しろ、という話をした。続きの話として、unsignedの有効性について述べてみようと思う。 負がないけどsignedの倍までいけるぜ! なんてのは、大した話ではない。今日びそんなことを誰が気にするというのか。世の中はみんな富豪なのだ。2Gで足りなければ4Gなんてセコいことを言う奴を尻目に迷わずlong longと打つし、それで駄目なら迷うことなくGNU mp(gmp)だろう。 unsignedで唯一良いところは、境界チェックが楽だというのがある。これも充分セコいけどね。 つまり配列の添字みたいな値を0〜1024に制限したいとする。signed intなnが与えられると、

if(0<=n && n<1024){
  // OK
}

といったコードになるだろう。しかし、unsigned intなnならば、

if(n<1024){
  // OK
}

で済む。なぜならアンダーランも<1024でどうにかできるからである。-1は4294967295なので<1024の真偽だけでチェックできる。 しかしこれってチェックするなら比較のときだけキャストすりゃいいんだよね。

if((unsigned)n<1024){
  // OK
}

見た目は増えてるけど、例えばx86だと単に比較の後のbranchのインストラクションが変わるだけだから、2回比較するよりも2倍高速(!)というわけで、巷でよく使われているテクニックだと思う。計算機がいろいろとzero-originになってるのはこのためというのもあるんだよ!(へぇへぇへぇ) …って、普段はsigned intで持って必要なときだけキャストしろって話か。結局unsigned擁護になってないよ(笑)。 プログラマって、暇な時間さえあればこういうセコいことをしようとするんですよね。だから余計にバグるのか!

信用できない計算機の話

なぜバグがなくならないのか。そこにはいろんな原因があるんだけど…という話をしよう。今回は一言で言えば、計算機がバカだから、という理由。 世の中に、浮動小数点数を信用していない人は多くいると思うけど、実は整数演算も信用できるものではない。 #include int main(int argc, char **argv) { unsigned int a=10,b=20; unsigned int c; c=a-b; printf(“d: %d-%d=%d\n”, a,b,c); printf(“u: %u-%u=%u\n”, a,b,c); if(c > 3){ printf(“yes1! %u > 3\n”, c); } if(a-b > 3){ printf(“yes2! %u-%u > 3\n”, a, b); } return 0; } gcc -Wallでワーニングもなく粛々とコンパイルされたその麗しき実行ファイルは、以下のような出力をしてくれる。別に不思議なことは何も…ない!? d: 10-20=-10 u: 10-20=4294967286 yes1! 4294967286 > 3 yes2! 10-20 > 3 10-20 > 3なのだ。奴等はそう信じているのだ。整数はダメ! 疑え! ダウトだ! これだから計算機というものは信用できない(笑)。 実際にあった失敗例としては、タイムアウトを判定するときに時刻情報をunsignedな整数で定義していて、時刻情報同士で引き算をして比較する。でもって何かの拍子に時刻が狂うと(例えばntpはこういうことをけっこうする)、いきなりタイムアウトが発生してしまう。ntpが時刻を戻すときって、逆転しないように徐々に進みを遅くしていくって話を昔聞いたんだけどなぁ。そういうのは特別な設定だけなのかもしれない。 というわけで、「time_tを64bitにする前にとりあえずunsignedにして延命しよう」なんてバカな考えには賛同できない。人類の英知を持ってすれば、2038年までなら64bitにできるはずだ(地球では2038年1月19日にtime()==0x7fffffffを迎える)。 まあ、要はunsignedなんて使うなアホってな話になるだけなんだけど(笑)。なんでも「負の数はありえない=unsigned」だと思っていると、まんまと騙される。ファイルサイズとか。配列の長さとか。とにかくunsignedは使うな。っていうかむしろコンパイラがはやくunsignedを禁止するのがよろしかろう。若者よ、あんなのしょせんはゴマカシなのだ。はやく目を覚ませ(笑)。 賢い人は、size_tがunsignedなのを見て、バカばっかだな、と思うことだろう。size_tを引き算したらダメだ。これ基本。sizeof(x)も引いたらダメ。strlen()の戻り値も引いたらダメ。htonl() やhtons()も引いたらダメだ。幼稚な計算機に引き算などという高等なことはできないのだ。そこが人間様との大きな違いだ(笑)。 はやくC言語に標準で無限桁の整数演算を定義してほしい。そして国会議員は一刻も早くunsignedを法律で禁止してほしい。その法律こそがバグを半分に減らす魔法の法律として永遠に語り継がれるであろう。 …なんだそりゃ。 (追記) 2004-08-20 15:58 gccはsizeof()をunsignedにしますが、例えばtcc(Tiny C Compiler)(free.fr)はsizeof()をsigned intとして扱ってくれるので、sizeof() に関してはtccと心中する気のある人は引き算しても大丈夫です。どっちが仕様に従っていないのか、あるいはどっちでもいいのかは不明です。

メモ:Pythonで代替関数の定義

存在しないメンバを参照しようとするとAttributeError例外が出るので、単にそれを受け取って代替の関数で上書きしちゃえばいい。

try:
  get_major=os.major
  get_minor=os.major
except AttributeError:
  def get_major(x):
    return x/0x100
  def get_minor(x):
    return x%0x100
  os.major=get_major
  os.minor=get_minor

os.majorとos.minorはPython2.3から入った。2.2ではexceptの中が実行される。中身は、たぶん妥当に動く場合が多いと思うけど、もちろん保証の限りではない。 ここで軽い注意。

def os.major(x):
  return x/0x100

と直接書くことはできない(まあ、書かないとは思うけど)。 モジュールの関数の場合はmodule.func()で呼んでも第1引数はselfにならないようだ。クラスの場合はいわゆるC++でもおなじみの暗黙の第1引数ということでobj.func()は第1引数がobj(いわゆるthisとかselfとか呼ばれるモノ)になるのだが。で、classの中にクラスの外で定義したfuncを押し込むときは、引数にselfを書けばいい。 ちなみに(関係ないが)Pythonではselfは予約語ではない。

\0

30年弱ほど生きてみて、やっぱりこの結論に至った。といきなり大きく振りかぶったわりには小さな結論だが、、、 やっぱりC言語の文字列は間違いだよ。null terminateされた文字列…間違いだよ、やっぱり。文字列が\0を含んじゃいけないのか? strlen()は暴走してもいいのか? djbあたりもそう言ってる(たぶん)。

意外とない…

複数のRSSをマージするプログラムって、あまり転がってませんね。どうせXMLだから自力でもどうにかなるか、というわけで、rssmerge.pyを書いてみました。けっこう適当なので、あまり使いものになりませんでしょう。途中で何かに失敗したまま突き進んだ感もかなり(笑)。しかもいろんな仕様もあんまり見ないで書いてるからそのへんも適当だし。DOMってこういう使い方でいいんだっけ?? っていうかインターネットに転がってるXMLをまっとうなXMLパーサでパースしちゃダメかも(笑)。ココログのAtomとか食わすと、Pythonのxml.dom.minidomが例外吐くもんね。うちのサイトのRSSもエラーになる(UTF-8じゃないと食べられないのねminidom…) これでurllibかなんかでネットから落としてくるのと、雑多なオプション(新タイトルやURLの設定とか)を加えたらそこそこ見られるようにもなるかもな…一応、現状でもRSS0.91とRSS1.0とAtom0.3の相互変換に使えないこともないか。 (追記) 2004-07-05 23:11 ところで4SuiteってPyXML関係のパッケージだったんですね。いつもインストールするときに一瞬表示されて、これ何だろうって思ってました。4DOMだの4XSLTだの4XPathだの4RDFだの4XPointerだのが含まれてる、真面目なXML関係ライブラリらしいです。

MozillaのブックマークをOPMLに変換する、あるいはOPMLのツリー構造の特定のパスを取り出す

恥ずかしげもなく、bm2opml.pyopmlsplit.pyをとりあえずさらしておきます。 それぞれ、Mozilla(firefoxでしか試していない)のブックマークをOPMLに変換する、含まれる情報がかなり落ちる上に非常に適当なスクリプトと、OPMLファイルってのはフォルダ分けされていてツリー構造になってることが多いんですが、その特定の階層を抜き出すというスクリプト。探したんだけど、意外と見つからないのでだらだらと自作して。

# python bm2opml.py < ~/.mozilla/firefox/default.wfs/bookmarks.html > bm.opml
# python opmlsplit.py bm.opml '/top/RSS' > rss.opml
# python opmlsplit.py rss.opml '/RSS/サッカー/J2' > j2.opml

みたいに。非常にてきとうに、ブックマークの中の「RSS」というフォルダ以下がRSSのブックマーク、それ以外はHTMLのブックマークということにしてあります。OPMLの構文はよくわからないのでやはりテキトーです(笑)。まあ参考程度にでも。 MozillaのブックマークがXMLのパーサを通ってくれればちゃんとやるとこなんですけどね。galeon時代は良かった…。そういえばXBEL形式ってどこ行っちゃったんだろう。