C ++宏将可读方程转换为相应的代码

时间:2013-08-13 15:56:49

标签: c++ macros readability

假设我目前有以下代码:

double P[2][2][10]; 
std::vector<double> b, r, n; 

//
// Assume that 10 doubles are pushed to each vector and
// that P has all its allocated values set.
//    

for(int t=0; t<10; ++t) {
    P[0][0][t] = b[t]*r[t]+n[t];
    P[0][1][t] = b[t]*2.0*r[t]+(1.0-n[t]);
    P[1][0][t] = b[t]*b[t]+r[t]*n[t];
    P[1][1][t] = r[t]+n[t];
}

这是一个简单的例子来说明我的问题。在实际情况中,P通常为P[9][9][100],方程式会更加混乱。我的问题是,基本上,我如何使用宏来使这些方程更具可读性?

特别是,这是一个非工作代码片段,用于说明我希望这个问题的解决方案看起来如何:

#define P(i,j) P[i][j][t]
#define b b[t]
#define r r[t]
#define n n[t]

for(int t=0; t<10; ++t) {
    P(0,0) = b*r+n;
    P(0,1) = b*2.0*r+(1.0-n);
    P(1,0) = b*b+r*n;
    P(1,1) = r+n;
}

此代码片段至少存在一个问题。例如,它将根据宏定义扩展For循环语句中的“r”和“n”。但你明白了。

这里的目标是开发一种输入方程式的方法,可以更容易地读取和检查错误。我对非宏解决方案持开放态度,虽然在我看来宏可能对此有所帮助。


关于我上面发布的非工作代码片段,以说明我想象一个解决方案可能看起来......是否可以使用宏,只有在For-loop体内才会发生宏替换?或者至少在之后 For循环语句?

5 个答案:

答案 0 :(得分:2)

一个有效的解决方案是在for之前定义所有宏,然后在for之后定义#undef所有宏。例如:

#define P(i,j) P[i][j][t]
#define b b[t]
#define r r[t]
#define n n[t]

for(int t=0; t<10; ++t) {
    P(0,0) = b*r+n;
    P(0,1) = b*2.0*r+(1.0-n);
    P(1,0) = b*b+r*n;
    P(1,1) = r+n;
}

#undef P
#undef b
#undef r
#undef n

在我看来,宏不是这个问题的最佳解决方案,但我找不到任何其他解决方案。

答案 1 :(得分:2)

将概念上不同的东西分开通常是一个好主意。这通常可以提高代码清晰度,可维护性和灵活性。

这里至少有两件不同的事情:

  1. 循环遍历数据数组。

  2. 你计算了一些东西。

  3. 你能做的最好的事情就是把这些东西分成不同的功能,或者更好的是,类。这样的事情可以做到:

    class MyFavoriteMatrix
    {
      private:
        double m_P[2][2];
      public:
        MyFavoriteMatrix( double b, double r, double n ) {
          m_P[0][0] = b*r+n;
          m_P[0][1] = b*2.0*r+(1.0-n);
          m_P[1][0] = b*b+r*n;
          m_P[1][1] = r+n;
        }
        double Get( int i, int j ) {
          return m_P[i][j];
        }
    }
    

    然后你的循环看起来像这样:

    for(int t = 0; t < 10; ++t)
    {
      // Computation is performed in the constructor
      MyFavoriteMatrix mfm( b[t], r[t], n[t] );
    
      // Now put the result where it belongs
      P[0][0][t] = mfm.Get( 0, 0 );
      P[0][1][t] = mfm.Get( 0, 1 );
      P[1][0][t] = mfm.Get( 1, 0 );
      P[1][1][t] = mfm.Get( 1, 1 );
    }
    

    注意这些解决方案:

    1. 如果您改变主意关于存储容器(例如,如Mark B建议的那样,出于性能原因将P [2] [2] [10]更改为P [10] [2] [2] ,你的计算代码根本不会受到影响,只有相对简单的循环会有所改变。

    2. 如果您需要在10个不同的地方执行相同的计算,则无需复制计算代码:您只需在那里调用MyFavoriteMatrix。

    3. 你发现计算代码需要改变你只需要在一个地方修改它:在MyFavoriteMatrix构造函数中。

    4. 每段代码看起来都很整洁,因此错字率很低。

    5. 所有你通过分离概念上不同的东西得到的 - 计算和迭代。

答案 2 :(得分:1)

这里有好的老式循环局部变量有什么问题?我假设你把你的向量称为比b更有意义的东西,所以我会给它们稍长的名字。我还冒昧地在你非常密集的方程式中添加了一些清晰的空格:

double P_arr[10][2][2]; 
std::vector<double> b_arr, r_arr, n_arr; 

//
// Assume that 10 doubles are pushed to each vector and
// that P has all its allocated values set.
//    

for(int t = 0; t < 10; ++t)
{
    const double b = b_arr[t];
    const double r = r_arr[t];
    const double n = n_arr[t];
    double** P = P_arr[t];

    P[0][0] = b * r + n;
    P[0][1] = b * 2.0 * r + (1.0 - n);
    P[1][0] = b * b + r * n;
    P[1][1] = r + n;
}

答案 3 :(得分:1)

惊讶没有人建议使用参考文献。 http://en.wikipedia.org/wiki/Reference_(C%2B%2B)

typedef double Array22[2][2]; // for convenience...

for(int t = 0; t < 10; ++t)
{
    const double &b(b_arr[t]);
    const double &r(r_arr[t]);
    const double &n(n_arr[t]);
    Array22 &P(P_arr[t]);

    P[0][0] = b * r + n;
    P[0][1] = b * 2.0 * r + (1.0 - n);
    P[1][0] = b * b + r * n;
    P[1][1] = r + n;
}

答案 4 :(得分:0)

这是运营商设计的帮助。以下是add和multiply运算符的示例。您仍然需要根据需要添加其他内容。

#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

std::vector<int>& operator+(std::vector<int>& a, std::vector<int>& b) {
    std::transform (a.begin(), a.end(), b.begin(), a.begin(), std::plus<int>());
    return a;
}

std::vector<int>& operator*(std::vector<int>& a, std::vector<int>& b) {
    std::transform (a.begin(), a.end(), b.begin(), a.begin(), std::multiplies<int>());
    return a;
}

int main() {
    int a[6] = {1,2,3,4,5,6};
    int b[6] = {6,7,8,9,10,11};
    std::vector<int> foo(a, a+6);
    std::vector<int> bar(b, b+6);

    foo = foo + bar;
    std::cout << foo[0] <<std::endl;
}