Я все еще новичок в 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;
}
Я был в некотором роде озадачен тем, как модулировать вещи, а затем скомпилировать их через 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 +=
Заявление не является необходимым.
Надеюсь, что это может помочь добавить некоторую ясность.
За исключением замены магических чисел на константы в начале, мало что можно сделать, чтобы улучшить научный код, так как едва ли что-нибудь пригодно для повторного использования.
Единственная часть, которая повторяется:
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 строк), вы ничего не можете сделать, кроме извлечения частей своего кода, которые являются очень абстрактными. Следующим шагом является написание класса для муравьев и того, что когда-либо делают эти муравьи, но для этого мало пользы, если у вас больше кода.
Это не код спагетти вообще. Структура управления на самом деле довольно прямолинейна (ряд циклов, иногда вложенных). Из того, как используются 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
). Это может выглядеть немного сложнее, но можно сделать намного проще с помощью пары tyepdef
s. В конечном счете, разработчики C ++ будут понимать код лучше, чем они, если вы будете настаивать на использовании rarray
,
Посмотрите внимательно на операции, которые работают со стандартными контейнерами C ++. Например, строительство и изменение размера std::vector
— по умолчанию — инициализирует элементы нулями во многих случаях. Вы могли бы заменить некоторые наборы вложенных циклов одним оператором.
Также покопайтесь в стандартных алгоритмах (в шапке algorithm
). Они могут действовать на целый ряд элементов в любом std::vector
— через итераторы — и, возможно, делать другие вещи напрямую, для которых этот код нуждается во вложенных циклах.