1

私はまだ C++ の初心者であり、与えられたスパゲッティ コードをモジュール化しようとしています。これまでのところ (git の使い方を学んだり、rarray ライブラリをインストールして自動配列をそれらに置き換えたりすることは別として)、物事をモジュール化し、それを 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

モジュール化してから make でコンパイルする方法について、私はちょっと困惑しています。

これは、モジュール化しようとしているコードが原因である可能性があります。モジュール化は、コードの 1 つの領域に問題があった場合に別の領域に影響を与えるとは限らないように、問題のドメインを分離するためによく使用されるイディオムであり、大規模なアプリケーションを構築する場合に特に役立ちます。モジュール化は、オブジェクト指向設計におけるクラスの重要なポイントの 1 つでもあります。

*必然的に「スパゲッティ化」に関して、つまり、コードが本当に「スパゲッティ コード」である場合、多くの場合、コードの 1 つの領域を変更または修正すると、意図しないまたは予期しない結果を伴うコードの他の領域に確実に影響します。つまり、モジュラーではありません。

あなたが投稿したコードは 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++ とは多少異なっていたか、または元のコードはその言語の機能をあまり活用していなかったのでしょう。

モジュール化したい場合は、いくつかのものを適切に名前を付けた個別の関数に分割することを検討してください。

魔法の値 ( 35635600.3401.9など) を取り除きます。それらを名前付き定数 (コンパイル時に修正する場合) または名前付き変数 (将来のある時点でコードへの入力として使用する可能性が十分にある場合) に変換します。C または C++ では実際には標準ではないことM_PIに注意してください (多くの C および C++ 実装に共通ですが、標準ではないため、すべてのコンパイラで動作することが保証されているわけではありません)。

何が何でrarrayあるかを理解し、それを標準の C++ コンテナーに置き換える方法を考え出します。私の推測では、使用法から、 はrarray<float, 2> number_if_ants(356,356)float の 2 次元配列を表し、両方の次元が に等しいということ356です。std::vector<std::vector<float> >そのため、 (C++ の任意のバージョン) または (C++11 の場合) std::array<std::array<float, dimension>, dimension> (ここdimensionで、 の魔法の値を置き換える任意の名前)を使用することが適切な場合があります356。少し複雑に見えるかもしれませんが、いくつかの の助けを借りて、はるかに簡単にすることができますtyepdef。長い目で見れば、C++ 開発者は、どうしてもrarray.

C++ 標準コンテナーで機能する操作を注意深く見てください。たとえば、a の構築とサイズ変更は、std::vectorデフォルトでは、多くの状況で要素をゼロに初期化します。ネストされたループのセットの一部を単一のステートメントに置き換えることができる場合があります。

また、標準アルゴリズムを掘り下げます (ヘッダー内algorithm)。それらは、イテレータを介して任意の範囲の要素に作用し、std::vectorおそらくこのコードがネストされたループを必要とする他のことを直接実行できます。

于 2016-01-26T05:26:03.000 に答える