2016-04-22 4 views
7

2次元配列を扱う場合。あなたは要素を頻繁に訪問する必要があります。これを行うための真っ直ぐな方法は、2つのネストされたループによるものです。行列をループするためにCでDRYの原理を実装する方法

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

このコードの原則は、しばしばコード全体にわたって繰り返しコピーされます。これをどのように解決してドライになるのですか?これを解決する唯一の方法は、ビジター関数を関数ポインタで使用することです

編集:より建設的になるには、マトリックスタイプがtypedef double** Matrix;であると仮定してください。 Loop over matrix elements applying variable function

+0

これらのループを生成するマクロを作成することができます。 – Crozin

+0

はい、この場合、関数またはマクロを使用できます。 –

+0

隠されたvarsを作成した場合、マクロはおそらく乱雑で(読者にはわかりません)、隠れた 'i'と' j'varsを呼び出すか、作成します(読者には明白ではありません)。入れ子にできませんでした。 – DaveRandom

答えて

3

最初の仕事::シンプルtypedef、行列を表すstructとしてdataを作り直す、または、その失敗を考慮するC++がこの方法で解決される可能性があるために

。私はあなたが前者をやると仮定します。

// do something with data[i][j]」はiをとる関数foo言う)、j、及び引数としてマトリックスstructへのポインタであってもよいです。

その後、あなただけのループを行う一つの機能が必要になります。その関数は、適切なfoo機能ポインタを取り、マトリックスstructポインタ。

あなたの仕事は、あなたの要求に応じてさまざまなfooを実装することです。

このためではない使用マクロを実行します。彼らはijなどのハードコーディングされた変数名を導入する場合は特に、困難なデバッグを行うこと。

+0

うーん、私はカスタマイズされた訪問者を作るためにブーストバインドのようなものが必要だと思います。これまでネストされた関数(これは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。おそらくそのためにvariadic関数を使用する可能性がありますか? – math

+0

variadic関数の実装は次のようになります。http://pastebin.com/N8JtCfve – math

0

ヒントを与えてくれたBathshebaのおかげで、私は最終的に私が自信を持っていない関数ポインタを含むソリューションを思いつきました。主な問題は、追加のパラメータが必要な特定の関数の作成です。

注:joopのおかげで、マクロソリューションでは省略可能な追加の関数呼び出しが必要になります。

たとえば、このような訪問者は、すべての行列要素を所定の値でリセットします。別の訪問者は、要素のセットをパラメータで変更したり、単一のパラメータを必要としない場合もあります。ですから、基本的には、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); 
} 

可変引数機能:

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

のvoidポインタ:

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

関連する問題