我现在想养成良好的编码习惯。下面的代码是在严格的约束下编写的。我们在课堂上做作业不是为了讨论“真实世界”的最佳实践--我们只是在很少或没有反馈的情况下得到字母等级。有哪些好问题的例子,我可以问自己,以确定什么时候,为一个分配的代码将重新编写在现实世界的应用程序?
(我对内存管理也很感兴趣,我们还没有在课堂上讨论过。任何内存管理提示re:下面的代码将是超级感谢!)
注意:这是一个完成的任务,我并不是在寻找帮助来完成它,只是帮助尽早养成良好的习惯。
任务:
main()之外,只使用一个函数-- dice_roll()。所有的计算都是内联的。enums、输出精度以及rand()和srand()函数。enum定义赢、输,并继续给出每个滚动的结果。const数据大小:int SIZE和int ROLLS。请随时提供您认为合适的任何评论/问题。
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>
// this is what will hold our data during the games
struct nodeType
{
int num_rolls;
int win;
int loss;
nodeType* link;
};
// these pointers will be used to traverse and perform operations
// on the nodes as we figure out what to do with them
nodeType *first, *last, *current, *trailCurrent, *newNode, *temp;
int roll_dice();
int main()
{
enum gameOutcome { CONTINUE, WIN, LOSE };
gameOutcome game_result;
const int MAX_GAMES(5500);
int sum, point, roll;
first = NULL; // start of our list
last = NULL; // end of our list
srand(time(0)); // give rand() new seed
for (int i = 1; i <= MAX_GAMES; ++i) // this for loop simulates all games
{
sum = roll_dice();
newNode = new nodeType; // create new node for this game
newNode->link = NULL; // make sure it doesn't point to anything
switch (sum) // first roll test
{
case 7:
game_result = WIN;
roll = 1;
case 11:
game_result = WIN;
roll = 1;
break;
case 2:
game_result = LOSE;
roll = 1;
case 3:
game_result = LOSE;
roll = 1;
case 12:
game_result = LOSE;
roll = 1;
break;
default:
game_result = CONTINUE;
point = sum;
roll = 1;
break;
} // end of switch
while (game_result == CONTINUE) // if no win/lose after 1st roll
{
sum = roll_dice();
++roll;
if (sum == point)
game_result = WIN;
else if (sum == 7)
game_result = LOSE;
}
// these assignments prepare our node fields to accept the
// game outcome data
newNode->num_rolls = roll;
newNode->win = 0;
newNode->loss = 0;
if(game_result == WIN)
newNode->win = 1; // adds one win for unique # of rolls
else
newNode->loss = 1; // adds one loss for unique # of rolls
if(first == NULL) // if empty list creates list on first roll
{
first = newNode;
last = newNode;
}
else
{
current = first; // starts search at beginning of list
int found(0); // use for list elem search
while (current != NULL && found < 1) // search to insert ascending order
{
if (current->num_rolls == roll) // has a game w/ this number of rolls
{ // been played?
if (game_result == WIN)
current->win += 1; // if so add one win to that "row"
else
current->loss += 1; // if so add one loss to that "row"
found = 1;
}
else if (current->num_rolls > roll) // if game's #rolls is < than some
found = 2; // #rolls in the list
else
{
trailCurrent = current;
current = current->link; // advances the search one node
}
} // end of while
if (found == 1)
delete[] newNode; // if #rolls for complete game already exists,
// delete this node. this is like "deduping" a
// database to first normal form
else if (current == first) // inserts node at beginning of list
{
newNode->link = first;
first = newNode;
}
else
{
trailCurrent->link = newNode; // inserts node in middle of list
newNode->link = current;
if (current == NULL) // if it's the biggest #rolls so far
last = newNode; // insert node at end of list
} // end of last else
} // end of 1st else
} // end of main for loop
int sum_wins(0), sum_loss(0);
current = first; // set to first node in list before iterating over it
while (current != NULL) // print results and sum wins/losses
{
std::cout << std::setw(4) << current->win << " games won and "
<< std::setw(3) << current->loss << " games lost on roll "
<< current->num_rolls << std::endl;
sum_wins += current->win; // summing wins for use below
sum_loss += current->loss; // summing losses for use below
current = current->link;
}
// calculate the odds based on game results
std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint)
<< "\nodds of winning are " << sum_wins << " / "
<< sum_wins + sum_loss << " = " << std::setprecision(2)
<< 100.0 * sum_wins / (sum_wins + sum_loss) << "%." << std::endl;
// calculate avg num rolls per game
double avg_length(0);
current = first;
while (current != NULL)
{
avg_length += (current->win + current->loss) * current->num_rolls;
current = current->link;
}
std::cout << "avg num rolls/game is " << std::setprecision(2)
<< avg_length / 5500.00 << " rolls." << std::endl;
while (first != NULL) // destroy list
{
temp = first;
first = first->link;
delete[] temp;
}
last = NULL;
std::cout << "press RET to exit";
std::cin.get();
return 0;
} // end of int main()
int roll_dice()
{
return (rand() % 6) + (rand() % 6) + 2;
}发布于 2013-05-21 03:08:29
有很多事情你想问自己。第一个问题是,“在现实世界中,这些要求有意义吗?”在这种情况下,答案是强调“不”。
除了main()之外,只使用一个函数--
dice_roll()。所有的计算都是内联的。
这是一个可怕的要求,无论是谁写了这份作业,都需要用常识的蝙蝠来解决。这是从一开始就教不好的练习。拥有一个巨大的main函数是不可取的。
要问的第二个问题是,“我是使用C++,还是使用C与std::cout一起使用,而不是printf?”这很可能不是你的错--任何决定将学生的所有代码打包到main函数中的老师,充其量都是在做一项可疑的工作。
让我们解决这两个问题。如果你想学习,我要说的是扔掉这些要求中的一些。该方案的真正要求是:
模拟5500个骰子游戏并打印结果。
在这里,需求本身导致程序结构相对容易崩溃。我们将保持它的简单性,并且说我们总共需要4个函数:
roll_dice() (已经存在)的函数run_simulation。这将以要玩的游戏数量作为参数。main。C++的优点之一是标准库中提供了丰富的数据结构。它们通常为您执行内存管理。它不是在需求中列出的,但是从您的代码中,您想要存储给定数量的按顺序排列的滚动的赢/输数。
同样,我不确定您在数据结构方面有多少经验,但是,标准库很适合这个需求:std::map。映射以一种有效的方式存储键/值对,因此键查找是快速的。
好的,我们将一如既往地开始:定义我们的enum和用来存储游戏数据的东西--调用它(缺乏想象力) game_data:
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>
#include <map> // <--- Additional include so we can use `std::map`
enum game_outcome { CONTINUE, WIN, LOSE };
struct game_data
{
int win;
int loss;
game_data()
: win(0),
loss(0)
{ }
game_data(int w, int l)
: win(w),
loss(l)
{ }
};上面,我们为我们的game_data类型定义了一些构造函数。现在,我们将定义我们的map类型:
typedef std::map<int, game_data> result_list;这表明result_list类型是从整数到键的映射。在这里,int键将是滚动的数量。map的一个好处是它按排序顺序存储东西--所以我们的排序是免费的。
int roll_dice()
{
return (rand() % 6) + (rand() % 6) + 2;
}
result_list run_simulation(int num_games)
{
result_list results;
static game_outcome outcome[] = {CONTINUE, CONTINUE, LOSE, LOSE, CONTINUE, CONTINUE,
CONTINUE, WIN, CONTINUE, CONTINUE, CONTINUE, WIN,
LOSE};
for(int i = 0; i < num_games; ++i) {
int roll = 1;
int sum = roll_dice();
game_outcome out = outcome[sum];
while (out == CONTINUE) {
int point = sum;
sum = roll_dice();
++roll;
if(sum == point) {
out = WIN;
}
else if(sum == 7) {
out = LOSE;
}
}
game_data& d = results[roll];
(out == WIN) ? ++d.win : ++d.loss;
}
return results;
}首先,我们已经用数组查找代替了switch语句,这使得代码更加简洁,并且可以说不容易出错(在开关语句的末尾很容易忘记break )。除了一些重新排序之外,while循环基本上没有变化。最主要的改变是用这两行代码替换了很多代码:
game_data& d = results[roll];
(out == WIN) ? ++d.win : ++d.loss;map中的查找可以使用[]语法完成(非常类似于数组)。如果本例中不存在密钥,则插入该键。因此,我们使用rolls查找数据。例如,如果rolls为3,这将找到映射中的game_data类,其中包含前面的结果。然后,我们只需根据结果增加正确的win或loss。
现在,我们需要编写一个函数,它将获取地图中的内容,并从其中打印出一些信息:让我们将其命名为result_statistics。
void result_statistics(const result_list& results, int num_games)
{
int wins = 0;
int losses = 0;
double avg_length = 0;
for(auto const & p : results) {
std::cout << std::setw(4) << p.second.win << " games won and "
<< std::setw(3) << p.second.loss << " games lost on roll "
<< p.first << "\n";
wins += p.second.win;
losses += p.second.loss;
avg_length += (p.second.win + p.second.loss) * p.first;
}
int total_games = wins + losses;
std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint)
<< "\nodds of winning are " << wins << " / "
<< total_games << " = " << std::setprecision(2)
<< 100.0 * wins / total_games << "%." << "\n";
std::cout << "avg num rolls/game is " << std::setprecision(2)
<< avg_length / num_games << " rolls." << "\n";
}打印代码基本上是一样的。
最后,我们的新main函数:
int main()
{
const int kGames = 5500;
srand(time(nullptr));
result_list r = run_simulation(kGames);
result_statistics(r, kGames);
}请注意,我们现在可以(至少在较高级别上)跟踪代码流。我们定义游戏的总数,种子随机生成器,运行模拟,并计算统计从这些运行。以这种方式将事情分解,使事情更容易推理并在代码中遵循。
即使其中的一些(或大部分)对您来说还没有意义,我认为主要的事情是,如果您被告知将所有东西都塞进一个main函数中,那么如果这是必需的,就为您的任务做。然后,返回并将其分解为更小的函数,每个函数都有一个特定的、定义明确的、单一的用途。比较这两个人的代码。哪一个更容易理解?研究标准库,尽可能多地教自己(基本容器可能是目前最重要的事情)。最后,看看作业规格。它们都有意义吗?它们会导致多少可读性的代码吗?如果你有自由统治,你会采取什么不同的做法?
https://codereview.stackexchange.com/questions/26395
复制相似问题