6

double の配列の配列として表されるデータ ポイントのセットがあるとします。

double **data;

ここで、各データ ポイントの一部のフィールド、たとえば 2番目のフィールドでデータを並べ替えたい場合は、次のようなコンパレータを記述します。

int compare_data_second_field(void *a, void *b) {
    double da = ((double *) a)[1];
    double db = ((double *) b)[1];
    if (da < db) return -1;
    else if (da > db) return 1;
    return 0;
}

を使用qsortして、2番目のフィールドで並べ替えます。

私の質問は、並べ替えたいフィールドを事前に知らない場合、これをどのように一般化するかです。場合によっては1番目のフィールドで並べ替え、場合によっては 5番目のフィールドで並べ替えたい場合もあります。また、スレッド セーフにしたいので、並べ替えるフィールドを追跡するためにグローバル変数を使用したくありません。これらの複数が同時に進行する可能性があるためです。

C++ では、カスタムの並べ替えクラスを使用し、そのクラスにインスタンス変数を使用して、並べ替えるフィールドを追跡します。Cでこのようなことをする方法がわかりません。

4

3 に答える 3

7

qsort_rお使いのプラットフォームで利用できる場合は、それを使用するのが最善の方法です。qsort_rコンパレータに渡される追加の引数を受け入れるため、それを使用して、データを並べ替えるフィールドを渡すことができます。

プラットフォームでそれが利用できない場合、これを行う簡単な方法はありません。グローバル変数を使用したり、並べ替えフィールドに関する情報を含む構造体にデータをラップしたり、独自の のqsort_rような関数をローリングしたりして、この問題を回避できます。

于 2012-07-01T18:45:18.593 に答える
4

実際には、ネストされた関数(GCC 拡張機能) を使用した、これに対する非常に優れた解決策があります。
あなたができることは、一般的なコンパレータを作成することです:

int my_comparator(const void* a, const void* b, int n)
{
    double da = ((double*)a)[n];
    double db = ((double*)b)[n];
    return (da > db) ? 1 : ((da < db) ? -1 : 0);  /* Awesome */
}

オリジナルをラップするカスタムソート関数qsort()

void my_qsort(void* base, size_t num, size_t size,
    int (*comparator)(const void *, const void *, int), int field)
{
    /* Internal comperator */
    int my_qsort_comperator(const void* a, const void* b)
    {
        return comparator(a, b, field);
    }

    /* Invoke the base qsort function */
    qsort(base, num, size, my_qsort_comperator);
}

この関数は、ソートするフィールドのインデックスを示すqsort()追加の引数を取ることを除いて、元の と同じように動作します。field

于 2012-07-01T20:43:35.280 に答える
0

compare_data_field_NN = 0,1,2...の関数全体を宣言compare_dataしてから、対応する関数で初期化された関数ポインターの配列を宣言できます。次に、qsort特定のフィールドに対して、配列から関数ポインターをプルして qsort に渡します。マクロを使用して、関数と配列の生成をより簡単にすることができます。

#define REP10(M) M(0) M(1) M(2) M(3) M(4) M(5) M(6) M(7) M(8) M(9)
#define DECLARE_COMPARE(N)                                         \
    int compare_data_field_##N(void *a, void *b) {                 \
        double da = ((double *) a)[N];                             \
        double db = ((double *) b)[N];                             \
        if (da < db) return -1;                                    \
        else if (da > db) return 1;                                \
        return 0;                                                  \
    }
#define REF_COMPARE(N) compare_data_field_##N,

REP10(DECLARE_COMPARE)
int (*compare_data_field[])(void *, void *) = { REP10(REF_COMPARE) };

10 を超える可能性のあるフィールドが必要な場合は、REP10 マクロのみを変更する必要があります。

于 2012-07-01T19:45:15.813 に答える