2016-04-22 24 views
7

W przypadku tablic dwuwymiarowych, np. macierzy trzeba często odwiedzać elementy. Prosto naprzód sposobem na to jest przez dwóch zagnieżdżonych pętli:Jak zaimplementować zasadę DRY w C dla pętli nad macierzami

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

Zasada kod następnie jest często kopiowany w kółko przez cały kod. Jak rozwiązać ten problem, aby stać się SUCHĄ? Myślę, że jedynym sposobem rozwiązania tego problemu jest użycie funkcji gościa ze wskaźnikami funkcji, prawda?

Edytuj: Aby być bardziej konstruktywnym, załóżmy, że masz macierz typu typedef double** Matrix;.

Dla C++ to może być rozwiązany w ten sposób: Loop over matrix elements applying variable function

+0

Można utworzyć makro, które wygeneruje te pętle. – Crozin

+0

Tak, w takim przypadku można użyć funkcji lub makra. –

+0

Makro najprawdopodobniej byłoby niechlujne (nieoczywiste dla czytelnika) do wywołania lub utworzyłoby widmowe ukryte 'i' i' j' vars (również nieoczywiste dla czytelnika), Jeśli utworzysz ukryte vary, oni również nie można zagnieździć. – DaveRandom

Odpowiedz

3

Pierwsza praca: rozważyć przekształcenie data jako struct reprezentująca macierz, lub, w przypadku jego braku, prosty typedef. Zakładam, że robisz to pierwsze.

"// do something with data[i][j]" może być funkcja (a foo Say), które wykonuje i, j i wskaźnik do matrycy struct jako argumenty.

Potem trzeba tylko jedną funkcję, która wykonuje pętle: że funkcja przyjmuje funkcji wskaźnik do odpowiedniego foo, a wskaźnik matryca struct.

Twoim zadaniem jest wdrożenie różnych foo s, zgodnie z Twoimi wymaganiami.

Czy nie makra użyć do tego: one sprawiają, że debugowanie trudne, zwłaszcza jeżeli wprowadzą ciężko kodowane nazwy zmiennych takich jak i i j.

+0

Hmm, myślę, że potrzebuję czegoś takiego, jak wiązanie przypominające, aby dostosować odwiedzających. Do tej pory funkcje zagnieżdżone (które są rozszerzeniem GCC Jestem w połowie drogi: http://pastebin.com/3rUz9gEH) Ale nie będę używał makr dla kodu zgodnego z normami zagnieżdżonych funkcji ani zależności bibliotek takich jak http://www.haible.de/bruno/packages-ffcall.html, patrz np http://stackoverflow.com/questions/216037/what-tools-are-there-for-functional-programming-in-c. Może jego możliwe jest użycie funkcji variadic? – math

+0

Implementacja z funkcjami variadic mogłaby wyglądać następująco: http://pastebin.com/N8JtCfve – math

0

Dzięki Batszebie za udzielenie wskazówek, w końcu wymyśliłem rozwiązania dotyczące wskaźników funkcyjnych, z którymi nie jestem pewien. Głównym problemem jest tworzenie konkretnych funkcji, które mogą wymagać dodatkowych parametrów.

Uwaga: Dzięki Joop potrzebujemy więc dodatkowego wywołania funkcji, które można pominąć przy pomocy rozwiązania makro.

Na przykład taki użytkownik resetuje wszystkie elementy macierzy o danej wartości. Inny użytkownik może modyfikować elementy za pomocą zestawu parametrów lub nawet nie musi mieć jednego parametru. Więc w zasadzie mam do czynienia z pytaniem o elastyczną definicję typu funkcji vistor.

Przy okazji: W języku C++ można to rozwiązać za pomocą std::bind lub z templates.

zagnieżdżone funkcje:

funkcje zagnieżdżone są rozszerzeniem GCC, a na przykład niedostępne z Clangiem.Niemniej jednak tutaj jest przykład kodu:

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); 
} 

o zmiennej liczbie argumentów funkcje:

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); 
} 

nieważne wskaźnik:

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); 
} 

Być może istnieją inne sposoby, myślę o użyciu funkcji statycznej zmienne do inicjalizacji funkcji odwiedzającej, również obejmujące funkcje wariancji.

Dziękujemy za opinię.

+0

Zwróć uwagę, że funkcje zwrotne nie będą wstawiane, więc rzeczywiste wywołanie funkcji będzie wymagane dla każdego elementu macierzy. – joop