简体   繁体   English

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

[英]C++ macros to convert readable equations into corresponding code

Suppose I have the following code currently: 假设我当前有以下代码:

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

This is a trivial example to illustrate my question. 这是一个琐碎的例子来说明我的问题。 In real cases, P will often be P[9][9][100] and the equations will be a little more messy. 在实际情况下, P通常为P[9][9][100] ,而方程式会更加混乱。 My question is, basically, how can I use macros to make these equations more readable? 我的问题是,基本上,如何使用宏使这些方程式更具可读性?

In particular, here is a non-working code fragment to illustrate how I would like the solution to this question to look: 特别是,这是一个无法正常工作的代码片段,用于说明我希望如何解决此问题:

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

There is at least one problem with this code fragment. 此代码片段至少存在一个问题。 For example, it will expand the "r" and "n" in the For-loop statement according to the macro definition. 例如,它将根据宏定义在For循环语句中扩展“ r”和“ n”。 But you get the point. 但是你明白了。

The goal here is to develop a method for entering in equations that can be more easily read and checked for errors. 此处的目标是开发一种输入方程式的方法,该方法可以更轻松地读取和检查错误。 I'm open to non-macro solutions, though it seems to me that macros could be helpful here. 我愿意接受非宏解决方案,尽管在我看来宏在这里可能会有所帮助。


With respect to the non-working code fragment I posted above to illustrate how I imagine a solution might look... is it possible to use macros in such a way that macro substitution occurs only inside the For-loop body? 关于我上面发布的非工作代码片段,以说明我如何看待解决方案……是否可以以仅在For循环体内进行宏替换的方式使用宏? or at least not until after the For-loop statement? 或者至少是在for循环语句之后

A posible solution is defining all the macros just before the for and then #undef all macros after the for. 一种可行的解决方案是在for之前定义所有宏,然后在for之后#undef所有宏。 Example: 例:

#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

In my opinion, macros are not the best solution for this problem, but I cannot find any other solution. 我认为,宏并不是解决此问题的最佳解决方案,但我找不到其他解决方案。

What's wrong with good old-fashioned loop-local variables here? 这里好的老式循环局部变量怎么了? I'm going to assume that you call your vectors something more meaningful than b so I'll give them slightly longer names. 我假设您称向量比b更有意义,所以我给它们起长一点的名字。 I also took the liberty of adding some eye-clarity-granting whitespace in your very dense equations: 我还自由地在非常密集的方程式中添加了一些可以保证明晰度的空白:

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

It's usually a good idea to separate conceptually different things. 分开概念上不同的东西通常是一个好主意。 This usually goes a long way to improve code clarity, maintainability, and flexibility. 通常,这样做可以大大提高代码的清晰度,可维护性和灵活性。

There are at least two different things here: 这里至少有两件事:

  1. You loop through arrays of data. 您遍历数据数组。

  2. You compute something. 你计算一下。

The best thing you could do is to separate these things into different functions, or better yet, classes. 您可能要做的最好的事情是将这些东西分成不同的函数,或者更好的是,将它们分类。 Something like this would do: 这样的事情会做:

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

Then your loop would look like this: 然后,您的循环将如下所示:

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

Notice these things about such solution: 请注意有关此类解决方案的这些事项:

  1. If you change your mind about the storage containers (for example, as Mark B suggested, change P[2][2][10] to P[10][2][2] for performance reasons), you computational code won't be affected at all, only the relatively simple loop will change a bit. 如果您对存储容器改变了主意(例如,如Mark B所建议的,出于性能原因将P [2] [2] [10]更改为P [10] [2] [2]),那么计算代码将不会根本不会受到影响,只有相对简单的循环会有所改变。

  2. If you need to perform the same computation in 10 different places you won't have to copy the computational code: you'll just call MyFavoriteMatrix there. 如果需要在10个不同的位置执行相同的计算,则不必复制计算代码:只需在此处调用MyFavoriteMatrix。

  3. You you find out that the computational code needs to change you only need to modify it in one place: in MyFavoriteMatrix constructor. 您会发现计算代码需要更改,只需要在一个地方进行修改:在MyFavoriteMatrix构造函数中。

  4. Each piece of code looks neat, thus less chances for a typo. 每段代码看起来都很整洁,因此打字错误的机会更少。

And all that you get by separating conceptually different thing - computation and iteration. 通过分离概念上不同的内容(计算和迭代),您将获得所有收益。

Surprised nobody's suggested using references. 没有人对使用参考的建议感到惊讶。 http://en.wikipedia.org/wiki/Reference_(C%2B%2B) 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;
}

This is what operators are design to help with. 这就是运营商旨在提供帮助的内容。 Here is an example with add and multiply operator. 这是加和乘运算符的示例。 You will still need a add other as needed. 您仍然需要根据需要添加其他内容。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM