1

我仍然是 C++ 的新手,我一直在尝试模块化一些给我的意大利面条代码。到目前为止(除了学习如何使用 git 和安装 rarray 库以用它们替换自动数组)我对如何模块化事物然后通过 make 编译它感到有点困惑。

我知道我必须在头文件中创建原型,从我的函数创建我的目标文件,然后用“驱动程序”代码编译它。运行/编写 make 文件不是我关心的问题,而是如何开始像这样模块化代码;我不确定如何制作修改数组的函数!

任何指向正确方向的指针都会令人惊叹。如有必要,我可以澄清更多。

#include <cmath>
#include <iostream>
#include <rarray> // Including the rarray library.
#include <rarrayio> // rarray input/output, if necessary. Probably not.
int main()
{
    // ants walk on a table
    rarray<float,2> number_of_ants(356,356);
    rarray<float,2> new_number_of_ants(356,356);
    rarray<float,2> velocity_of_ants(356,356);
    const int total_ants = 1010; // initial number of ants
    // initialize
    for (int i=0;i<356;i++) {
        for (int j=0;j<356;j++) {
            velocity_of_ants[i][j] = M_PI*(sin((2*M_PI*(i+j))/3560)+1);
        }
    }
    int n = 0;
    float z = 0;
    for (int i=0;i<356;i++) {
        for (int j=0;j<356;j++) {
            number_of_ants[i][j] = 0.0;
        }
    }
    while (n < total_ants) {
        for (int i=0;i<356;i++) {
            for (int j=0;j<356;j++) {
                z += sin(0.3*(i+j));
                if (z>1 and n!=total_ants) {
                    number_of_ants[i][j] += 1;
                    n += 1;
                }
            }
        }
    }
    // run simulation
    for (int t = 0; t < 40; t++) {
        float totants = 0.0;
        for (int i=0;i<356;i++) {
            for (int j=0;j<356;j++) {
                totants += number_of_ants[i][j];
            }
        }
        std::cout << t<< " " << totants << std::endl;
        for (int i=0;i<356;i++) {
            for (int j=0;j<356;j++) {
                new_number_of_ants[i][j] = 0.0;
            }
        }
        for (int i=0;i<356;i++) {
            for (int j=0;j<356;j++) {
                int di = 1.9*sin(velocity_of_ants[i][j]);
                int dj = 1.9*cos(velocity_of_ants[i][j]);
                int i2 = i + di;
                int j2 = j + dj;
                // some ants do not walk
                new_number_of_ants[i][j]+=0.8*number_of_ants[i][j];
                // the rest of the ants walk, but some fall of the table
                if (i2>0 and i2>=356 and j2<0 and j2>=356) {
                    new_number_of_ants[i2][j2]+=0.2*number_of_ants[i][j];
                }
            }
        }
        for (int i=0;i<356;i++) {
            for (int j=0;j<356;j++) {
                number_of_ants[i][j] = new_number_of_ants[i][j];
                totants += number_of_ants[i][j];
            }
        }
    }
    return 0;
}             
4

3 回答 3

1

除了一开始用常量替换幻数之外,没有太多可以改进科学代码的方法,因为几乎没有任何东西是可重用的。

唯一重复的部分是:

for (int i=0;i<356;i++) {
    for (int j=0;j<356;j++) {
        new_number_of_ants[i][j] = 0.0;
    }
}

您可以将其提取为函数(我没有替换幻数,您应该先这样做并将它们作为参数提供):

void zeroRarray(rarray<float, 2> number_of_ants) {
    for (int i = 0; i < 356; i++) {
        for (int j = 0; j < 356; j++) {
            number_of_ants[i][j] = 0.0;
        }
    }
}

并调用:

zeroRarray(number_of_ants); // Btw the name of this rarray is misleading!

此外,用函数调用替换数学表达式:

velocity_of_ants[i][j] = M_PI* (sin((2 * M_PI * (i + j)) / 3560) + 1);

和:

velocity_of_ants[i][j] = calculateSomething(i, j);

函数看起来像:

double calculateSomethingHere(int i, int j) {
    return M_PI * (sin((2 * M_PI * (i + j)) / 3560) + 1);
}

因此,您可以给出这些长而有见地的名称,并专注于代码的每个部分的作用,而不是它的外观。

大多数 IDE 都内置了重构功能,您可以在其中突出显示要提取的部分代码,然后右键单击并选择从重构中提取函数(或类似的东西)。

如果您的代码很短(例如不到 200 行),除了提取非常抽象的部分代码外,您无能为力。下一步是为蚂蚁编写一个类以及这些蚂蚁在做什么,但是除非您有更多代码,否则这样做几乎没有什么好处。

于 2016-01-26T05:34:42.280 回答
1

对于如何模块化事物然后通过 make 编译它,我有点难过。

这可能部分是由于您尝试模块化的代码。模块化是一种习惯用法,通常用于帮助分离问题域,这样如果一个代码区域出现问题,它不一定*影响另一个区域,并且在构建更大的应用程序时特别有用;模块化也是面向对象设计中类的关键点之一。

*对于“意大利面条化”,即如果代码真的是“意大利面条代码”,经常修改或修复一个代码区域肯定会影响其他代码区域,从而产生意想不到或不可预见的后果,换句话说,不是模块化的。

您发布的代码是 63 行(主要功能),实际上不需要任何模块化。虽然如果你愿意,你会想看看什么可以模块化,什么应该模块化,但同样,除了制作单独的函数(这只会添加到代码批量)。既然你特别问

我不确定如何制作修改数组的函数!

这可以通过以下方式完成:

// to pass a variable by reference (so as to avoid making copies), just give the type with the & symbol
void run_simulation(rarray<float,2>& noa, rarray<float,2>& new_noa, rarray<float,2>& voa)
{
    // do something with the arrays
}

int main()
{
    // ants walk on a table
    rarray<float,2> number_of_ants(356,356);
    rarray<float,2> new_number_of_ants(356,356);
    rarray<float,2> velocity_of_ants(356,356);
    ...
    run_simulation(number_of_ants, new_number_of_ants, velocity_of_ants);
    ...
}

此外,应该注意您的代码中存在潜在的错误;在run simulation循环下,您声明float totants = 0.0;然后对该变量执行操作,直到循环结束,此时您仍然使用totants += number_of_ants[i][j];. 如果要使用此变量保持“运行”总数而不被重置,则需要将totants声明移到for循环之外,否则,严格来说,最后一条totants +=语句是不必要的。

希望这可以帮助增加一些清晰度。

于 2016-01-26T05:54:52.927 回答
0

这根本不是意大利面条代码。控制结构实际上非常简单(一系列循环,有时是嵌套的)。从使用 csome 结构的方式来看,它已经从其他一些编程语言翻译成 C++,而无需付出太多努力就可以将其从原始语言转换为“有效的 C++”(即,它是用另一种语言的技术编写的 C++)。但我的猜测是原始语言与 C++ 有些不同——或者原始代码没有大量使用该语言的特性。

如果你想将它模块化,考虑将一些东西分解成单独的、适当命名的函数。

摆脱魔法值(如356, 3560, 0.3, 40, 1.9, 等)。将它们转换为命名常量(如果它们要在编译时修复)或命名变量(如果有合理的机会,您可能希望它们在将来的某个时间成为代码的输入)。请记住,这M_PI在 C 或 C++ 中实际上并不是标准的(它在许多 C 和 C++ 实现中很常见,但不是标准的,因此不能保证适用于所有编译器)。

找出是什么rarray,并找出如何用标准 C++ 容器替换它。我的猜测是,从用法来看,它rarray<float, 2> number_if_ants(356,356)代表一个二维浮点数组,两个维度都等于356. 因此,使用std::vector<std::vector<float> >(任何版本的 C++)或(在 C++11 中)std::array<std::array<float, dimension>, dimension>dimension我的任意名称来替换你的魔法值356)可能是合适的。这可能看起来有点复杂,但可以在几个tyepdefs 的帮助下变得更简单。从长远来看,C++ 开发人员会比你坚持使用rarray.

仔细查看适用于 C++ 标准容器的操作。例如,std::vector在许多情况下,默认情况下 a 的构造和调整大小会将元素初始化为零。您也许可以用一条语句替换一些嵌套循环集。

此外,深入研究标准算法(在 header 中algorithm)。它们可以通过迭代器作用于任何元素中的一系列元素,std::vector并且可能直接执行此代码需要嵌套循环的其他事情。

于 2016-01-26T05:26:03.000 回答