Модульный код спагетти

Я все еще новичок в 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;
}

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 += Заявление не является необходимым.

Надеюсь, что это может помочь добавить некоторую ясность.

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 имеют встроенную функцию рефакторинга, где вы выделяете часть кода, которую вы хотите извлечь, и щелкаете правой кнопкой мыши и выбираете функцию Извлечь из Refactor (или что-то подобное).

Если ваш код короткий (например, менее 200 строк), вы ничего не можете сделать, кроме извлечения частей своего кода, которые являются очень абстрактными. Следующим шагом является написание класса для муравьев и того, что когда-либо делают эти муравьи, но для этого мало пользы, если у вас больше кода.

1

Это не код спагетти вообще. Структура управления на самом деле довольно прямолинейна (ряд циклов, иногда вложенных). Из того, как используются 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 — по умолчанию — инициализирует элементы нулями во многих случаях. Вы могли бы заменить некоторые наборы вложенных циклов одним оператором.

Также покопайтесь в стандартных алгоритмах (в шапке algorithm). Они могут действовать на целый ряд элементов в любом std::vector — через итераторы — и, возможно, делать другие вещи напрямую, для которых этот код нуждается во вложенных циклах.

0
По вопросам рекламы [email protected]