Реализация общей функции 'map' над массивами в C - PullRequest
10 голосов
/ 29 октября 2010

У меня проблемы с реализацией универсальной функции 'map' над массивами.Я начал со следующего черновика:

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem)
{
   unsigned int i = 0, j = 0;
   void * temp = malloc(elem);

   for(i = 0; i<n, i++)
   {
      temp = (f)((char *) src) + i));
      for(j = 0; j < elem; j++)
      {
         *(((char *) dest) + i) = *(((char *) temp) + i);
      }
   }
   free(temp);
}

Я понимаю, почему это не правильно - я использую (char *) перед тем, как передать его в 'f' - но я сейчас демотивирован и не могупредставить решение.(Я делаю это в процессе изучения C)

Мое обоснование состояло в том, чтобы получить результат 'f' и побайтно скопировать его в dest [i].

Можете ли вы дать мне подсказку?

Ответы [ 3 ]

18 голосов
/ 29 октября 2010

Ваша первая проблема в том, что вы делаете слишком много в нескольких выражениях. Вы должны сломать это.

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem)
{
   unsigned int i = 0, j = 0;
   void * temp = malloc(elem);

   char* csrc = (char*)src;
   char* cdest = (char*)dest;
   char* ctemp = (char*)temp;
   for(i = 0; i<n; i++)
   {
       csrc++;
       cdest++;
       ctemp++;
       temp = f(csrc);
       for(j = 0; j < elem; j++)
       {
           cdest[i] = ctemp[i];
       }
   }
   free(temp);
}

Теперь ваша вторая проблема. Вы Malloc буфер, а затем вы ... назначить этот указатель? Несколько раз? Тогда освободите только результат последнего звонка? Это совершенно не нужно.

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem)
{
   unsigned int i = 0, j = 0;

   char* csrc = (char*)src;
   char* cdest = (char*)dest;
   for(i = 0; i<n; i++)
   {
       csrc++;
       cdest++;
       char* ctemp = (char*)f(csrc);
       for(j = 0; j < elem; j++)
       {
           cdest[i] = ctemp[i];
       }
   }
}

Теперь ваша третья проблема. Вы передаете указатель - но только на символ. Вы не проходите в пустоту *. Это означает, что ваша функция не может быть универсальной - f не может применяться ни к чему. Нам нужен массив void * s, чтобы функция могла принимать любой тип в качестве аргумента. Нам также нужно взять размер типа в качестве аргумента, чтобы мы знали, как далеко продвигаться по dest.

void MapArray(void ** src, void * dest, void * (f)(void *), size_t n, size_t sizeofT)
{
    for(unsigned int i = 0; i < n; i++) {
        void* temp = f(src[n]);
        memcpy(dest, temp, sizeofT);
        dest = (char*)dest + sizeofT;
    }
}

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

void MapArray(void ** src, void * dest, void * (f)(void *, void*), void* userdata, size_t n, size_t sizeofT)
{
    for(unsigned int i = 0; i < n; i++) {
        void* temp = f(src[n], userdata);
        memcpy(dest, temp, sizeofT);
        dest = (char*)dest + sizeofT;
    }
}

Теперь f может оперировать практически всем, что ему нравится, и сохранять любое нужное ему состояние. Но мы все еще не освобождаем буфер. Теперь f возвращает простую структуру, которая сообщает нам, нужно ли нам освобождать буфер. Это также позволяет нам освобождать или не освобождать буфер при различных вызовах f.

typedef struct {
    void* data;
    int free;
} freturn;

void MapArray(void ** src, void * dest, freturn (f)(void *, void*), void* userdata, size_t n, size_t sizeofT)
{
    for(unsigned int i = 0; i < n; i++) {
        freturn thisreturn = f(src[n], userdata);
        void* temp = thisreturn.data;
        memcpy(dest, temp, sizeofT);
        dest = (char*)dest + sizeofT;
        if (thisreturn.free)
            free(temp);
    }
}

Однако я до сих пор не понимаю цели этой функции. Все это заменить простой цикл? Код, который вы пытаетесь заменить, проще, чем код для вызова вашей функции, и, возможно, более эффективен и определенно более мощный (например, они могут использовать continue / break).

Более того, Си действительно отстой для такой работы. С ++ намного лучше. Например, довольно просто применить функцию к каждому члену массива.

2 голосов
/ 29 октября 2010

Нет такой причины, как в стандартной библиотеке C - почти невозможно сделать это хорошо в C. Вы не можете скопировать результат "byte by byte" в dest[i] - так как выdest * * * *. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * '*' '' elem '). Предполагается, что *1007* соответствует размеру любого типа f иn - количество элементов в src и dest.В этом случае ваш код не так уж далек, но (как вы, вероятно, догадались) то, как вы манипулируете указателями (особенно приведением к char *), не приведет к его сокращению.

Однако, даже если вы исправите это, у вас будет еще одна проблема: назначить тип возврата из f, не зная, что тип будет действительно (действительно) трудным.На самом деле, о единственном способе, которым я могу придумать, это вместо этого обернуть этот код в макрос:

#define MapArray(s, d, f, n) \
do {                         \
   unsigned i;               \
   for (i = 0; i<n; i++)     \
      d[i] = f(s[i]);        \
} while (0)

Вы бы использовали это примерно так:

int f(int a) { return a + 100; }

#define elements(array) (sizeof(array)/sizeof(array[0]))

int main() { 
    unsigned i;
    int x[] = { 0, 1, 2, 3};
    int y[elements(x)];

    MapArray(x, y, f, elements(x));

    for (i=0; i<elements(x); i++)
        printf("%d\n", y[i]);
    return 0;
}

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

1 голос
/ 29 октября 2010
  • Убедитесь, что функция f не возвращает указатель на локальную переменную
  • Я не уверен, что цикл над j должен делать
...