2016-04-22 21 views
7

Quando si ha a che fare con array bidimensionali, ad es. matrici è necessario visitare gli elementi abbastanza spesso. Il modo dritto in avanti per farlo è da due cicli annidati:Come implementare il principio DRY in C per il looping su matrici

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

Questo principio codice poi è spesso copiato più e più volte in tutto il codice. Come risolvi questo per diventare ASCIUTTO? Penso che l'unico modo per risolverlo sia usare una funzione visitatore con i puntatori di funzione, giusto?

Modifica: per essere più costruttivo, si supponga di avere il tipo di matrice typedef double** Matrix;.

Per C++ questo potrebbe essere risolto in questo modo: Loop over matrix elements applying variable function

+0

È possibile creare una macro che generi questi loop. – Crozin

+0

Sì, è possibile utilizzare la funzione o la macro in questi casi. –

+0

Una macro sarebbe probabilmente disordinata (non ovvia per il lettore) da chiamare o creerebbe "i" e 'j' vars fantasma (anche non ovvio per il lettore), Se crei i vars nascosti, anche loro non può essere annidato. – DaveRandom

risposta

3

Primo lavoro: considerare rifusione data come struct rappresenta la matrice, o, in alternativa, un semplice typedef. Immagino che tu faccia il primo.

"// do something with data[i][j]" potrebbe essere una funzione (a foo dire) che prende i, j, e un puntatore alla matrice struct come argomenti.

allora avete bisogno solo una funzione che fa il loop: che funzione prende il puntatore funzione di ad un appropriato foo, e il puntatore di matrice struct.

Il vostro lavoro è quindi quello di implementare i vari foo s, in base alle vostre esigenze.

fare non utilizzare le macro per questo: fanno il debug difficile, soprattutto se si introducono i nomi delle variabili hard-coded come i e j.

+0

Hmm, penso di aver bisogno di qualcosa come boost bind per fare visitatori su misura. Finora con le funzioni annidate (che sono un'estensione GCC sono a metà strada lì: http://pastebin.com/3rUz9gEH) Ma non userò macro per il codice conforme alle norme delle funzioni nidificate, né una dipendenza da librerie come http://www.haible.de/bruno/packages-ffcall.html, vedi ad es http://stackoverflow.com/questions/216037/what-tools-are-there-for-functional-programming-in-c. Forse è possibile usare funzioni variadiche per quello? – math

+0

L'implementazione con le funzioni variad potrebbe essere la seguente: http://pastebin.com/N8JtCfve – math

0

Grazie a Bathsheba per dare suggerimenti, ho finalmente trovato soluzioni che prevedevano puntatori di funzione, di cui non sono sicuro. Il problema principale è la creazione di funzioni specifiche che potrebbero richiedere parametri aggiuntivi.

Nota: grazie a joop, è necessario quindi un richiamo di funzione aggiuntivo, che potrebbe essere omesso con una soluzione macro.

Ad esempio, un visitatore del genere reimposta tutti gli elementi della matrice con un determinato valore. Un altro visitatore potrebbe modificare gli elementi con un set di parametri o non avere nemmeno bisogno di un singolo parametro. Quindi fondamentalmente affronto la domanda su una definizione flessibile del tipo di funzione vistor.

BTW: in C++ questo può essere risolto con std::bind o con templates.

Funzioni nidificate:

funzioni nidificate sono un'estensione GCC, e ad esempio non disponibile con Clang.Tuttavia qui è il codice di esempio:

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

Funzione Variadica:

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

vuoto puntatore:

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

Forse esistono altri modi, penso di usare funzione statica variabili per l'inizializzazione della funzione visitatore, che coinvolgono anche funzioni variadiche.

Grazie per il vostro feedback.

+0

Si noti che le funzioni di callback non saranno in linea, quindi sarà richiesta una chiamata di funzione effettiva per ciascun elemento di matrice. – joop