mots quotidiens.
Daichi Mochihashi (持橋大地) daichi <at> ism.ac.jp by hns, version 2.10-pl1.

先月 2006年12月 来月
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

2006年12月04日(月) [n年日記]

#1 Natural Order in Unix

モデルなどの名前に "model.9" "model.10" .. などと数字を付けることは クラスタリング等でよくあると思いますが, この間IWSLT関係の仕事で, "model.0"〜"model.19" という名前がついたファイルを, それぞれ "model.1"〜"model.20" にリネームする必要があった。
こういう複雑なリネームには, Camel Book のP.426にあるperlの万能スクリプト rename を使って
for f in model.*; do
rename 's/\.(\d+)$/".".($1+1)/e' $f
done
とすればいいですが(マニアック!), 実は上のファイル名グロブ "model.*" は普通は 文字列としてソートされるため, "model.1" "model.10" "model.11" .. "model.2" のように展開されてしまうので, 上ではうまく行かない(ファイルがどれか失くなってしまう)。
Unixの "sort -n" は名前が全て数字の場合にはうまく働くが, 上の場合のように 文字列と混合されている場合には働かない。

ごく一般的に, 数字を含んだファイル名を数値の部分を考慮して並べたいことは よくあると思いますが, Macintosh System7 には数字を含んだ文字列を「自然に」ソートする ための Stuart Cheshire 氏による"Natural Order" という素晴らしいINITが1994年頃 からあって (上の画像), 機能拡張フォルダに入れておくだけで, システムの文字列比較関数を書き換えて, Finder等のファイルが期待した通りに並ぶようになる。
[Natural Order Numerical Sorting]

これには上の画像にあるようにソースが付いているので, 前からやろうと思っていた 通り, 週末にちょっと工作して Unix で使えるようにしよう.. と思って 少し調べたら, すでに誰かがやっていた;。まあ確かに, ごく自然に 考えつきそうなことではあるけれども..。
[Natural Order String Comparison] by Martin Pool 氏。
このページには perl, C, Haskell, Ruby, Python, Java, Javascript による実装例 が載っていて, GNU ls では, "--sort=version" を指定すると常に natural order ソートになるらしい。確認したところ, 確かにそうなる模様。
ただ, 上のファイル名グロブはシェルが行うものなので, 後は zsh にパッチを当てれば いいかな.. と思って下調べをしたら, これもすでにオプションが存在していた。 ガーン。;;

setopt numericglobsort
としておくと, 最初の例のようなファイル名展開が "model.1" "model.2" .. "model.10" のように「自然な」順番になる模様。 これはX68000版の zsh のマニュアルに含まれる zshintro.jp に書いてあるので 読んでいたはずなのだが, 何となく見落としていたらしい。(;_;)
Stuart Cheshire 氏の「Natural Order」は1994年で, Martin Pool氏のページは 2000年くらいに独立に作ったページのようですが, zsh には1994年の時点で既に numericglobsort のオプションは組み込まれていたようなので, zsh ユーザの慧眼に 改めて驚いた。

というわけで, まとめると

という手順で, Unix でも "Natural Order" によるソートを使うことができるようです。


*1: 上のページには GNU textutils の sort(1) に対するパッチもあるようですが, 今は sort(1) は GNU coreutils に含まれていて, アップデートの度に 毎回パッチを追従させるのは手間なので, 自分で別コマンドにして用意しておくとよさそうです。

2006年12月07日(木) [n年日記]

#1 debug

Cのプログラムでこれまではデバッグ情報を普通にstderrに書いていたが, そうするとプログラムの出力と混じってしまう上, 後から検証するのも難しくなる。
ふと気がついて, 下のような dprintf() 関数を書いて, 便利に使っている。
/*
    debug.c
    $Id: debug.c,v 1.3 2006/12/07 09:46:58 dmochiha Exp $

*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "debug.h"
#define  DEBUG_FILE     "log.debug"

static FILE *dp = NULL;

int
dprintf (const char *fmt, ...)
{
        int n;
        va_list ap;
        
        if (dp == NULL)
        {
                if ((dp = fopen(DEBUG_FILE, "w")) == NULL)
                {
                        fprintf(stderr, "dprintf: can't open debugging output: %s\n",
                                DEBUG_FILE);
                        exit(1);
                }
        }
        va_start(ap, fmt);
        n = vfprintf(dp, fmt, ap);
        fflush(dp);
        va_end(ap);
        return n;
}
何も考えず, 普通に
dprintf("table[%d] = %g\n", i, table[i]);
のようにすると, 固定ファイル(上では "log.debug")に結果が蓄積される。 必要なら, #ifdef DEBUG .. #endif で囲ってもいいかも。
この結果は途中で tail -f で見ることができるし(そのために上ではfflush() している), 結果も残るので, 後から検証するのにも便利。 プログラムの出力と混ざらないので, 実験がとてもしやすくなった。 *1

可変長引数を取るのは Lisp では常識に近いのに, C言語の普通の講座では ほとんど採り上げられていないような気がする。
最初にC言語を習った時に気になって 聞いたら, vararg.h を使えと言われたが, その当時はよくわからなくて使えなかった。 上は簡単な例ですが, 今頃になって大分わかってきたような気もする。


*1: 上でdpをクローズしていないのはわざとで, debug_finish()などを一々 実行しなくてすむようにするためです。


2 days displayed.
タイトル一覧
カテゴリ分類
 なかのひと
Powered by hns-2.10-pl1, HyperNikkiSystem Project