Создание функции общего режима в C - PullRequest
0 голосов
/ 23 октября 2018

Итак, я пытался создать универсальную функцию, которая вычисляет (математический) режим некоторого типа.У меня есть часть этого, но я пропускаю другие части, поэтому он не скомпилируется.Мне нужна помощь по заполнению этих недостающих частей.

Функция режима для целых чисел использует struct count для определения элемента и частоты.Его определение:

struct count {
    int value;          // the value of the number
    unsigned int freq;  // how many times the number has been seen
}

Целочисленная версия режима выводит массив, который содержит все связи для наиболее частого числа вместе с тем, сколько существует связей.

unsigned int mode(int* tiebuf, int* list, size_t listsize)
{
    struct count modelist[listsize];
    size_t modesize = 0;

    // initialize modelist[]
    for(int i = 0; i < listsize; i++)
    {
        int* found = search(list[i], modelist, modesize);    // signature: search(key, list, listsize)
        if(found == NULL)
        {
            (modelist[i]).num = &list[i];
            (modelist[i]).freq = 0;
             modesize++;
        }
        else
        {
            (*found).freq++;
        }
    }

    // take the most frequent element (last in modelist)
    qsort(modelist, listsize, sizeof(struct count), cmpfreq);
    int mode_element = listsize-1;

    // see if there are any ties for frequency
    size_t tiecount = 1;
    for(int i = mode_element-1; i > 0; i--)
    {
        if((modelist[i]).freq < (modelist[mode_element]).freq)
        {
            tiecount++;    // overshot by 1
            break;
        }
    }

    // output the tie as an array
    for(int i = 0; i < tiecount; i++)
    {
        tiebuf[i] = (modelist[mode_element-1-i].number);
    }
    return tiecount;    // returns how many elements are in tiebuf
}


int cmpfreq(const void* obj1, const void* obj2)
{
    struct count **t1 = (struct count**)obj1;
    struct count **t2 = (struct count**)obj2;
    return ( ((*t1)->freq) - ((*t2)->freq) );
}


int cmpnum(const void* obj1, const void* obj2)
{
    struct count **t1 = (struct count**)obj1;
    struct count **t2 = (struct count**)obj2;
    return ( ((*t1)->number) - ((*t2)->number) );
}


int* tiedmode( int* list, size_t listsize, int (*cmp)(const void*, const void*) )
{
    // takes the median of all ties in the mode
    int ties[listsize];
    int tiescount = mode(ties, list, listsize);
    qsort(ties, tiescount, cmpnum);    // this call doesn't work
                                       // want to call cmpnum with cmp as an argument
                                       // this is why we needed cmpnum
    int middle = tiescount/2;
    return ties[middle];
}

Теперь япланировать преобразование этого в общую запись.Первое, что нужно сделать, это изменить определение struct count

struct count {
    void* object;       // some object
    unsigned int freq;  // how many times that object has appeared
}

Сигнатура для режима также должна измениться на unsigned int mode(void* tiebuf, size_t tienum, void* list, size_t listsize, size_t objsize, int (*cmp)(const void*, const void*))

Большая проблема со вспомогательной функцией cmpnumи вот тут у меня проблемы.Поскольку для qsort требуется указатель функции с подписью int (*fnptr)(const void*, const void*), cmpnum также требует эту подпись.Однако для сравнения объектов cmpnum, вероятно, также необходим другой указатель функции, данный пользователем для того, чтобы сравнить их.В идеале функция cmpnum должна выглядеть следующим образом:

int cmpnum(const void* obj1, const void* obj2, int (*compare)(const void*, const void*))
{
    struct count** t1 = (struct count**)obj1;
    struct count** t2 = (struct count**)obj2;
    return ( compare(t1->object, t2->object) );
}  

Так как бы я привел указатель функции с 3 аргументами к указателю на функцию только с 2 аргументами?Или, еще лучше, как бы я решил проблему с несоответствием между * qsort и cmpnum?

РЕДАКТИРОВАТЬ: Причина, по которой мне нужно cmpnum, в первую очередь заключается виз-за tiedmode.Эта функция берет режим и выводит медиану связей.Чтобы найти медиану, мне нужно отсортировать по номеру.Но так как это будет сделано универсально, мне нужно, чтобы пользователь сообщил библиотеке, как сортировать объект внутри struct count.

1 Ответ

0 голосов
/ 23 октября 2018

Функция сравнения, которую вы передаете qsort, может только принимать два аргумента.

Это то, что вы делаете в своем верхнем блоке кода.

Для чеговы хотите, как во втором блоке кода, вам нужно создать столько вариантов cmpnum, сколько у вас будет *compare указателей.(например, cmpnum_type1, cmpnum_type2 и т. д.) Это "чистый" способ.

Другой способ [который немного грязный], это использовать ваш оригинальный cmpnum, но иметь compareуказатель подфункции будет global :

int (*compare) (const void *, const void *);

int
cmpnum(const void *obj1, const void *obj2)
{
    struct count **t1 = (struct count **) obj1;
    struct count **t2 = (struct count **) obj2;

    return (compare(t1->object, t2->object));
}

void
dosort(void)
{

    compare = foocmp;
    qsort(modelist, listsize, sizeof(struct count), cmpnum);

    compare = barcmp;
    qsort(modelist, listsize, sizeof(struct count), cmpnum);
}
...