【问题标题】:Modularizing spaghetti code模块化意大利面条代码
【发布时间】:2016-01-26 04:40:09
【问题描述】:

我还是 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;
}             

【问题讨论】:

  • 您应该能够在函数参数中传递对数组对象的引用。但是,如果您使用的是 C 数组,则数组本身就是一个指针,因此将它传递给您的函数就足够了。
  • 这将有助于首先用 cmets 识别每个代码块的用途,然后,您可以将这些块转换为函数并调用它们而不是原始代码。这一切都伴随着首先明确每个块存在的原因。
  • 正如发布的答案所说,这不是意大利面条代码。它实际上非常干净且易于理解。试图在不了解数组的情况下“修复”它将是一个巨大的错误。在你对如何做出你认为(现在)必要的改变非常有信心之前,甚至不要碰它。

标签: c++ modular modularization


【解决方案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]; 对其进行修改。如果此变量 is 用于保持“运行”总数而不被重置,则需要将 totants 声明移到 for 循环之外,否则,严格来说,最后一个totants += 声明是不必要的。

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

【讨论】:

    【解决方案2】:

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

    唯一重复的部分是:

    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 行),除了提取非常抽象的部分代码外,您无能为力。下一步是为蚂蚁编写一个类以及这些蚂蚁在做什么,但除非您有更多代码,否则这样做几乎没有什么好处。

    【讨论】:

      【解决方案3】:

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

      如果您想对其进行模块化,请考虑将一些东西分解为单独的、适当命名的函数。

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-13
        • 1970-01-01
        • 1970-01-01
        • 2010-09-16
        • 2012-01-26
        • 1970-01-01
        • 1970-01-01
        • 2010-11-02
        相关资源
        最近更新 更多