- そもそもなんで比較関数を呼び出すときにoptを渡せるようにする必要があるかを説明します。
- まずここに、学校で40人のクラスがあって、その成績があるとします。
struct Data_ {
int num[10];
char *nam;
} Data;
enum { NO, KOKU, SUU, EI, RI, SYA, ON, BI, GIKA, TAI }; // 出席番号, 国語, 数学, 英語, 理科, 社会, 音楽, 美術, 技術・家庭, 体育.
Data data[40] = { ... };
- これを国語の成績順に並べるとしたらどう書けばいいでしょうか?・・・まあこんな感じですよね。
int comp1a(const void *a, const void *b)
{
const Data *da = a, *db = b;
return da->num[KOKU] - db->num[KOKU]; // この書き方の場合、とりあえず低い順になる.
}
qsort(data, 40, sizeof (Data), comp1);
- では、算数の成績順に並べるとしたら?・・・こうですよね。
int comp2a(const void *a, const void *b)
{
const Data *da = a, *db = b;
return da->num[SUU] - db->num[SUU]; // この書き方の場合、とりあえず低い順になる.
}
qsort(data, 40, sizeof (Data), comp2);
- この調子で9教科+出席番号順のソートを実装すると、10個の比較関数を用意しなければいけません。さらに降順もサポートしたければ、引き算を逆にしたバージョンも用意する必要があるので、さらに倍増して20個の比較関数を用意することになります。それだけで120行も要してしまいます。これってすごく無駄だと思いませんか?バグを見逃す温床しかなりません。
- では、どうすればいいでしょうか?・・・そうです、比較関数が追加の引数を取れればいいのです。
int compAsc(void *opt, const void *a, const void *b)
{
int i = (int) opt;
const Data *da = a, *db = b;
return da->num[i] - db->num[i];
}
int compDesc(void *opt, const void *a, const void *b)
{
int i = (int) opt;
const Data *da = a, *db = b;
return db->num[i] - da->num[i];
}
- これがあれば、国語の成績順に並べるときは、
kqsort(data, 40, sizeof (Data), compAsc, (void *) KOKU);
- とやれば済むのです。もう新しい比較関数を作る必要はありません。つまり比較関数はたったの2つで済みます。合計14行です。実に106行も節約できるのです。
- このように、コールバック関数を関数に渡すときは、追加の引数として void * 型のパラメータを渡せるようにする方が一般です。・・・たとえばwin32のAPIにEnumWindows()というのがあるのですが、ちゃんと void * を渡せるようになっています。
- だから、むしろけしからんのはqsortのほうなのです。ということで、けしからんqsortを何とかしたものがkqsortになるわけです。