* kclib1のページ#2 -(by [[K]], 2019.04.16) ** (2) kqsort ** (4) kqsort -void kqsort(void *base, int num, int size, int (*compare)(void *, const void *, const void *), void *opt) --標準関数qsort()に対して、末尾に引数optを追加しました。これは比較関数compareを呼び出すときに、第一引数として渡します。それ以外はqsortと同じです。 ---- -[詳細説明] --そもそもなんで比較関数を呼び出すときに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になるわけです。 ---- -[効果]この関数を使うことで、比較関数をたくさん書かなければいけないケースが減ります。上記の例だと106行も節約されることになります。 ---- -[内部実装] #include <stdlib.h> static void *opt_; static int (*comp_)(void *, const void *, const void *); static int comp(const void *a, const void *b) { return comp_(opt_, a, b); } void kqsort(void *base, size_t num, size_t size, int (*compare)(void *, const void *, const void *), void *opt) { opt_ = opt; comp_ = compare; qsort(base, num, size, comp); } * こめんと欄 #comment