2016-04-22 4 views
7

При работе с двумерными массивами, например, матрицы, вам нужно часто посещать элементы. Прямой способ сделать это с помощью двух вложенных циклов:Как реализовать принцип DRY в C для петли над матрицами

for(int i=0; i < n; ++i) { 
    for(int j=0; j < m; ++j) { 
    // do something with data[i][j] 
    } 
} 

Этот принцип кода, то часто копируется снова и снова по всему коду. Как вы решаете это, чтобы стать СУХОЙ? Я думаю, что единственный способ решить это - использовать функцию посетителя с указателями функций, правильно?

Редактировать: Чтобы быть более конструктивным, допустим, что у вас есть матричный тип typedef double** Matrix;.

Для C++ это может быть решена следующим образом: Loop over matrix elements applying variable function

+0

Вы можете создать макрос, который будет генерировать эти циклы. – Crozin

+0

Да, вы могли бы использовать функцию или макрос в этих случаях. –

+0

Макрос, вероятно, будет либо беспорядочным (неочевидным для читателя), чтобы вызвать или создать фантомные скрытые «i» и «j» vars (также неочевидные для читателя). Если вы создаете скрытые vars, они также не может быть вложенным. – DaveRandom

ответ

3

Первая работа: рассмотреть переделывая data как struct представляющий матрицу, или, если это невозможно, просто typedef. Я предполагаю, что вы делаете первое.

«// do something with data[i][j]» может быть функцияfoo сказать), который принимает i, j, и указатель на матрицу struct в качестве аргументов.

Тогда вам нужно только одну функцию, которая делает зацикливание: эта функция принимает функцию указатель на соответствующий foo и указатель матрицы struct.

Ваша задача заключается в реализации различных foo s, в соответствии с вашими требованиями.

ли не использовать макросы для этого: они делают отладку трудно, особенно, если они вводят жестко закодированные имена переменных, как i и j.

+0

Хм, мне кажется, мне нужно что-то вроде boost bind, чтобы сделать с учетом посетителей. До сих пор с вложенными функциями (которые являются расширением GCC, я на половине пути: http://pastebin.com/3rUz9gEH). Но я не буду использовать макросы для стандартного кода вложенных функций, а также для зависимости от библиотеки, такой как http://www.haible.de/bruno/packages-ffcall.html, см., например, http://stackoverflow.com/questions/216037/what-tools-are-there-for-functional-programming-in-c. Может быть, возможно использовать для этого вариативные функции? – math

+0

Реализация с вариантными функциями может выглядеть так: http://pastebin.com/N8JtCfve – math

0

Благодаря Bathsheba за предоставленные советы, я, наконец, придумал решения с указателями функций, с которыми я не уверен. Основная проблема заключается в создании конкретных функций, которые могут потребовать дополнительных параметров.

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

Например, такой посетитель сбрасывает все элементы матрицы с заданным значением. Другой посетитель может изменять элементы с помощью набора параметров или даже не нуждаться в одном параметре. Поэтому в основном я сталкиваюсь с вопросом о гибком определении типа функции vistor.

BTW: В C++ это можно решить либо с помощью std::bind, либо с помощью templates.

Вложенные Функции:

Вложенные функции являются расширением GCC, и, например, недоступен в Clang.Тем не менее вот пример кода:

typedef double** Matrix;  
typedef void (*MatrixElementVisitor) (double* element); 

void visitMatrixElements(Matrix m, size_t rows, size_t cols, MatrixElementVisitor fn) { 
    for(size_t i = 0; i < rows; ++i) { 
    for(size_t j = 0; j < cols; ++j){ 
     fn(&m[i][j]); 
    } 
    } 
} 

void filM(Matrix m, size_t rows, size_t cols, double val) { 
    void fill(double *element) { 
    *element = val; 
    } 
    visitMatrixElements(m, rows, cols, fill); 
} 

VARIADIC функции:

typedef double** Matrix; 
typedef void (*MatrixElementVisitor) (double* element, va_list args); 

void visitMatrixElements(Matrix m, size_t rows, size_t cols, MatrixElementVisitor fn, ...) { 
    va_list args,copy; 
    va_start(args, fn); 
    for(size_t i = 0; i < rows; ++i) { 
    for(size_t j = 0; j < cols; ++j){ 
     va_copy(copy, args); 
     fn(&m[i][j], copy); 
     va_end(copy); 
    } 
    } 
    va_end(args); 
} 

void fill(double *element, va_list args) { 
    *element = va_arg(args, double); 
} 

void filM(Matrix m, size_t rows, size_t cols, double val) { 
    visitMatrixElements(m, rows, cols, fill, val); 
} 

недействительным указатель:

typedef double** Matrix; 
typedef void (*MatrixElementVisitor) (double* element, void *args); 

void visitMatrixElements(Matrix m, size_t rows, size_t cols, MatrixElementVisitor fn, void *args) { 
    if(m) { 
    for(size_t i = 0; i < rows; ++i) { 
     if(m[i]) { 
     for(size_t j = 0; j < cols; ++j){ 
      fn(&m[i][j], args); 
     } 
     } 
    } 
    } 
} 

void fill(double* element, void *args) { 
    if(!args) { 
    return; 
    } 
    *element = *((double*)args); 
} 


void filM(Matrix m, size_t rows, size_t cols, double val) { 
    visitMatrixElements(m, rows, cols, fill, &val); 
} 

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

Благодарим Вас за отзыв.

+0

Обратите внимание, что функции обратного вызова не будут встраиваться, поэтому для каждого элемента матрицы потребуется фактический вызов функции. – joop

Смежные вопросы