Как создать прототип, который ссылается на двойник, расположенный в структуре, расположенной в связанном списке? - PullRequest
0 голосов
/ 06 мая 2020

В настоящее время я создаю программу для интерполяции линейной регрессии по отсутствующим записям во временном ряду. IE Столбец 2, строка 20-30 отсутствует, программа возьмет столбец 2, строку 19 (например, 10) и столбец 2, строку 30 (20), а затем заполнит значения NULL линейно ie 11, 12, 13. У меня есть несколько столбцов, которые имеют значения NULL, поэтому для этого я хочу создать структуру

struct missingPoint
{
  double lastVal;
  struct node * ptrtoLast;
  int missingVals;
};
struct point
{
  double col1;
  double col2;
  double col3;
};

typdef struct point Tick;

typedef struct node
{
  Tick tick;
  struct node * next;
 } Node;

typedef Node * List;

Итак, идея состоит в том, чтобы написать прототип, а затем функцию, которая принимает * ptrtoList-> tick.colx в качестве аргумента как а также структура missingPoint, тогда я могу выполнить итерацию по столбцу и заполнить отсутствующие данные временного ряда, он выполняет итерацию столбца, хранящего ptrs, для узлов, которые содержат записи, отличные от NULL для Col, когда он достигает значения NULL, он имеет ptr до последнего узла с ненулевым значением val, он выполняет итерацию до тех пор, пока снова не получит значение, отличное от Null, затем, используя ptr, который он сохранил в памяти, он выполняет итерацию обратно и заменяет значения Null линейной регрессией между двумя точками. Но я не знаю, как я могу указать двойное значение, которое встречается внутри структуры, на которую указывает другая структура для функции и прототипа функции, с этой функцией я мог бы просто вызвать функцию для каждого столбца, который у меня есть, без него я ' мне придется жестко кодировать совсем немного, чего я бы хотел избежать. Мы будем очень благодарны за любые советы.

Итак, функция будет такой, в настоящее время столбец жестко закодирован, я хотел бы передать номер столбца в качестве аргумента, чтобы я мог его выполнить в многопоточном режиме, и вызвать функция несколько раз, поскольку матрицы, на которые я смотрю, довольно большие, и потому что я хотел бы попрактиковаться в параллельном программировании:

void crawlOne(List *plist)
{
  Node * last;
  double lastVal, tmp;
  int i, count = 0;
  Node * pnode = *plist;
  while(pnode != NULL)
  {
    last = pnode->next;
    pnode = pnode->next;
    if(pnode->tick.col1=NULL)
    {
      while(pnode->tick.col1=NULL)
      {
        count ++;
        pnode = pnode->next;
      }
      tmp = lastVal-pnode->tick.col1;
      pnode = last;
      for(i=0;i<count;i++)
      {
        pnode = pnode->next;
        pnode->tick.col1 = lastVal + i*(tmp/count);
        i++;
      }
    }
  }
}

Ответы [ 2 ]

0 голосов
/ 06 мая 2020

Итак, подведем итог:

  • У вас есть функция crawlOne(List *plist), которая находит некоторые объекты типа struct point и что-то делает с их col1 членами.

  • Вы хотели бы иметь функцию crawlSome(List *plist, int colnum), которая:

    • при вызове crawlSome(list, 1) работает с col1
    • при вызове crawlSome(list, 2) работает на col2
    • и так далее.

Безусловно, самый чистый подход, как предложил Джон Боллинджер в комментарии, - это перепроектировать struct point, чтобы он содержал массив вместо трех отдельных членов:

struct point {
    double col[3];
};

void crawlSome(List *plist, int colnum) {
    // ...
    pnode->tick.col[colnum] = ...;
}

Это означает, что вам нужно изменить весь существующий код, который использует struct point, что, хотя и просто, может быть утомительным, но в долгосрочной перспективе вы получите более чистый и эффективный дизайн. Я бы сделал это, если это вообще возможно.

Если вы действительно не можете изменить определение struct point (например, он используется сторонним кодом, который вы не можете изменить), тогда вы не можете избежать каким-то образом жестко запрограммировать имена членов, поскольку имена не существуют во время выполнения. KamilCuk предложил несколько вариантов для этого. Другой вариант, о котором я мог бы подумать, - это вытащить жесткое кодирование в функцию «селектора столбцов», чтобы это можно было сделать только в одном месте вашей программы. Он не должен быть слишком неэффективным, если его можно встроить.

inline double *select_column(struct point *pt, int colnum) {
    switch (colnum) {
        case 1: return &pt->col1;
        case 2: return &pt->col2;
        case 3: return &pt->col3;
        default: abort(); // or perhaps return NULL;
    }
}

void crawlSome(List *plist, int colnum) {
    // ...
    *select_column(&pnode->tick, colnum) = ...
}

Это позволяет избежать необходимости дублировать код crawlOne вообще, с макросом или без него.

Если вы хотите уменьшить количество повторений при определении select_column, вы можете использовать макрос с вставкой токена:

inline double *select_column(struct point *pt, int colnum) {
    switch (colnum) {
#define DO(N) case N: return &pt->col ## N ;
        DO(1)
        DO(2)
        DO(3)
#undef DO
        default: abort(); // or perhaps return NULL;
    }
}

Если вы хотите сделать селектор немного удобнее, вы можете обернуть его в макрос:

#define COL(p, n) (*select_column(&(p), (n)))

void crawlSome(List *plist, int colnum) {
    // ...
    COL(pnode->tick, colnum) = ...;
}

В качестве альтернативы аналогичный подход может быть реализован с помощью offsetof, хотя и с тем же отсутствием проверки типов, что указывает KamilCuk:

#include <stddef.h>

const size_t col_offsets[3] = {
    offsetof(struct point, col1),
    offsetof(struct point, col2),
    offsetof(struct point, col3)
};

#define COL(p, n) (*(double *)((char *)&(p) + col_offsets[(n)]))
0 голосов
/ 06 мая 2020
  • Обычно: просто определите функцию несколько раз. Нет, C не имеет шаблонов.
  • Лучшее: обобщите ваши данные, предоставив интерфейс для управления ими с помощью указателей функций - таким интерфейсом обычно является виртуальная таблица . В вашем случае достаточно одного указателя на функцию для доступа к базовым данным с помощью дескриптора чтения / записи. Это эффективно переносит вариантные / изменяющиеся / непостоянные части функции в другое место. Разрешите пользователям передавать дополнительный аргумент generi c, чтобы пользователи могли передавать настраиваемый контекст:

void crawlOne(List *plist, double *(*getcol)(void *arg, struct point *p), void *arg)
{
      // tmp = lastVal-pnode->tick.col1;
      double tmp = *getcol(arg, &lastVal-pnode->tick);
      ...
         // pnode->tick.col1 = lastVal + i*(tmp/count);
         *getcol(arg, &pnode->tick) = lastVal + i*(tmp/count);
      ...
}

double *point_getCol1(void *arg, struct point *p) {
    return &p->col1;
}
double *point_getCol2(void *arg, struct point *p) {
    return &p->col2;
}
double *point_getCol3(void *arg, struct point *p) {
    return &p->col3;
}

int main() {
    crawlOne(plist, point_getCol2, NULL);
    crawlOne(plist, point_getCol3, NULL);
}
  • В вашем случае вы можете передать offsetof в члены в point и разыменовать указатель на double* в нужных позициях. Это не является гибким и вызывает ошибки, потому что он не проверяет типы статически:

void crawlOne(List *plist, size_t coloffset)
{
  ...
      // tmp = lastVal-pnode->tick.col1;
      double tmp = *(double*)((char*)&lastVal-pnode->tick + coloffset);
      ...
         // pnode->tick.col1 = lastVal + i*(tmp/count);
         *(double*)((char*)&pnode->tick + coloffset) = lastVal + i*(tmp/count);
      ...
}

int main() {
    crawlOne(plist, offsetof(struct point, col1));
    crawlOne(plist, offsetof(struct point, col2));
}
  • Обычно для упрощения определения функции используется макрос. несколько раз (только если вы действительно знаете, что делаете). Такие, как правило, становятся сложными в обслуживании и отладке:

#define DECLARE_CRAWL_ONE(FUNC, MEMBER) \
void FUNC(List *plist) \
{ \
      /* tmp = lastVal-pnode->tick.col1; */ \
      double tmp = lastVal-pnode->tick.MEMBER; \
      ... \
         /* pnode->tick.col1 = lastVal + i*(tmp/count); */ \
         lastVal-pnode->tick.MEMBER = lastVal + i*(tmp/count); \
      ... \
}

DECLARE_CRAWL_ONE(crawlOnecol1, col1)
DECLARE_CRAWL_ONE(crawlOnecol2, col2)
DECLARE_CRAWL_ONE(crawlOnecol2, col2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...