软件工程(2019)结对编程第二次作业
一、题目要求
我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子,请实现它,要求:
能够自动生成四则运算练习题
可以定制题目数量
用户可以选择运算符
用户设置最大数(如十以内、百以内等)
用户选择是否有括号、是否有小数
用户选择输出方式(如输出到文件、打印机等)
最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)
二、任务分配
- 领航员: 王安宁
能够辅助驾驶员完成全部代码工作,并且为关键函数选用合适的覆盖标准设计测试用例,并编写代码进行单元自动测试。 - 驾驶员: 吴汶潞
能够完成全部代码工作,程序基本实现全部要求功能,并将代码上传至coding.net或者GitHub代码托管系统中。
三、功能实现
codingnet地址:https://dev.tencent.com/u/wwl5569335/p/sizeyunsuanzidongshengcheng/git
- 编程工具:Visual Studio 2017
1.可以定制题目数量
printf("请输入生成题目数量:\n");
while (true) {
cin >> num;
if (num > 0) break;
cout << "输入有误, 请重新输入题目数量:\n";
}
2.用户可以选择运算符
printf("请选择运算符, 例如:加减乘除 + - * / :\n ");
cin >> op;
//生成操作符
pos = rand() % opnum;
cout << op[pos];
//将操作符放入计算器;
s1.getCh(op[pos]);
flag++;
}
3.用户设置最大数(如十以内、百以内等)
printf("请输入数值的最大值 0 - x,(输入x必须为正数):\n");
while (true) {
cin >> max;
if (max > 0) break;
cout << "输入有误, 请重新输入最大值:\n";
}
4.用户选择是否有括号、是否有小数
bool cld1, cld2; // cld1为1 表示可能含有括号, 为0表示不含括号, cld2 为1表示含有小数为2表示不含有小数;
printf("请选择是否有括号 Y/N:\n");
cin >> cld1;
printf("========================================================\n");
printf("请选择是否有小数 Y/N:\n");
cin >> cld2;
四、程序代码
四则运算随机生成程序
#include "stdafx.h"
using namespace std;
int main()
{
srand((unsigned)time(NULL));
int num, max = 0;
char op[20] = { '\0' };
bool cld1, cld2; // cld1为1 表示可能含有括号, 为0表示不含括号, cld2 为1表示含有小数为2表示不含有小数;
float resArray[100] = { 0 };
int respos = 0;
// 输入操作
printf("请输入生成题目数量:\n");
while (true) {
cin >> num;
if (num > 0) break;
cout << "输入有误, 请重新输入题目数量:\n";
}
printf("========================================================\n");
printf("请选择运算符, 例如:加减乘除 + - * / :\n ");
cin >> op;
printf("========================================================\n");
printf("请输入数值的最大值 0 - x,(输入x必须为正数):\n");
while (true) {
cin >> max;
if (max > 0) break;
cout << "输入有误, 请重新输入最大值:\n";
}
printf("========================================================\n");
printf("请选择是否有括号 Y/N:\n");
cin >> cld1;
printf("========================================================\n");
printf("请选择是否有小数 Y/N:\n");
cin >> cld2;
// **********************************************--------***********//
//生成操作
int opnum = 0; //统计op 并处理
while (true) {
if (op[opnum] != '\0') {
opnum++;
}
else {
break;
}
} // 此时 opnum 记录着 op操作符的数量;
/*****************************************************************/
// opmax 为 + - * / 的最大个数, 限制了多项式的长度;
int pos = 0;
int opmax = 0;
int kuohao = 0;
int order = 1; //题目序号
int flag = 0; // 排除一个操作数左右两边括号的情况: (a) + b
Solve s1; //创建计算器, 用于实时计算字符串的值;
while (num--) {
s1.isolve(); //清空计算器;
s1.getCh('#'); // 放入'#' 准备接受算式开始计算;
float factor = 0;
cout << order++ << ". ";
pos = 0;
opmax = rand() % 6 + 1;
kuohao = 0;
while (opmax--) { //开始生成多项式
char vv = '\0';
bool lf = false; // 用于表示此次是否生成了左括号
if (cld1 == 1 && rand() % 8 > 5) { // 1/8 的概率出现括号;
flag = 0;
cout << '(';
kuohao++;
lf = true;
}
if (lf) {
s1.getCh('(');
}
factor = rand() % (max - 1) + 1 + ((rand() % 100)*1.0 / 100)* cld2;
cout << factor;
//生成操作数 factor;
s1.getNum(factor); //操作数放入计算器
bool rf = false; //用于表示是否生成了右括号;
if (cld1 == 1 && kuohao > 0 && flag != 0) {
if (rand() % 4 > 2) {
cout << ')';
kuohao--;
rf = true;
}
}
if (rf) {
s1.getCh(')');
}
//生成操作符
pos = rand() % opnum;
cout << op[pos];
//将操作符放入计算器;
s1.getCh(op[pos]);
flag++;
}
factor = rand() % (max - 1) + 1 + ((rand() % 100)*1.0 / 100)* cld2;
cout << factor;
//将最后一个操作数放入计算器
s1.getNum(factor);
while (kuohao--) {
cout << ')';
s1.getCh(')');
}
//将结尾符号'#'放入计算器;
s1.getCh('#');
printf("=?\n");
//将最终的结果放入结果数组
resArray[respos++] = s1.getRes();
}
printf("========================================================\n");
printf("||****************************************************||\n");
printf("计算结果如下:\n");
// 输出答案 *************************************************//:
for (int i = 0; i < respos; i++) {
cout << i + 1 << ". " << resArray[i] << endl;
if (i + 1 % 10 == 0) {
cout << endl;
}
}
printf("||****************************************************||\n");
printf("========================================================\n");
//******************************************************//
system("pause");
return 0;
}
运算结果生成程序
#include "stdafx.h"
using namespace std;
int Solve::table[7][7] = {// +, -, *, /, (, ), #,
{ -1, -1, -1, -1, 1, 0, 1 },
{ -1, -1, -1, -1, 1, 0, 1 },
{ 1, 1, -1, -1, 1, 0, 1 },
{ 1, 1, -1, -1, 1, 0, 1 },
{ 1, 1, 1, 1, 1, 0, 1 },
{ -1, -1, -1, -1, 0, 0 ,0 },
{-1, -1, -1, -1, 0, 0, 0}
};
Solve::Solve() { // 完成初始化操作符操作数堆栈置0
cst = new char[30];
fst = new float[30];
}
Solve::~Solve() {}
void Solve::isolve() {
cp = 0;
fp = 0;
}
void Solve::getNum(float c) {
fst[fp++] = c;
}
float Solve::getRes() {
return fst[--fp];
}
void Solve::getCh(char c) { // c 为新得到的字符;
if (cp == 0 && c == '#') { //接受第一个字符 '#'
cst[cp++] = c;
}
else { //若输入的是加减乘除'#';
char u = cst[cp - 1]; // u 栈顶字符
if (compare(c, u) == 1) { //新输入的优先级大于栈顶
cst[cp++] = c;
}
else if (compare(c, u) == 0) { // 新输入优先级小于栈顶
if (u == '(' && c == ')' || u == '#' && c == '#') {
cp--; //将左括号移出去;
}
else {
printf("priority 0 error");
}
}
else if (compare(c, u) == -1) { //新输入优先级等于小于栈顶
do {
cp--;
compute(u); //拿出栈顶符号去计算
u = cst[cp - 1]; //新的栈顶符号
} while (compare(c, u) == -1);
//直到栈顶符号优先级不高于 最新输入的操作符优先级,跳出循环
if (u == '(' && c == ')' || u == '#' && c == '#') {
//如果优先级相等,就拿出去;
cp--; //将左括号移出去;
}
else { //如果新输入操作符优先级高,则放入;
cst[cp++] = c;
}
}
else {
printf("error\n");
}
}
}
int Solve::compare(char a, char b) {
return table[getOdr(a)][getOdr(b)];
}
void Solve::compute(char c) {
float a, b;
float res;
b = fst[--fp];
a = fst[--fp];
if (c == '*') {
res = a * b;
}
else if (c == '/') { //现实世界除数为0是不被允许的,这里当除数为
//时当1处理,防止程序崩溃;
if (b == 0) {
res = a;
}
else {
res = 1.0 * a / b;
}
}
else if (c == '+') {
res = a + b;
}
else if (c == '-') {
res = a - b;
}
else {
printf("compute error having excepted opr");
}
fst[fp++] = res; //将计算成功的结果返回堆栈;
}
int Solve::getOdr(char c) {
if (c == '+') return 0;
if (c == '-') return 1;
if (c == '*') return 2;
if (c == '/') return 3;
if (c == '(') return 4;
if (c == ')') return 5;
if (c == '#') return 6;
}
测试程序
#pragma once
#include "stdafx.h"
class Solve{
private:
char *cst;
float *fst;
int cp, fp;
static int table[7][7]; //算数优先符号表
protected:
int compare(char a, char b);
/* a 为新输入操作符, b 为后输入操作符, 如果 a > b
返回 1, 如果 a = b 返回0; 如果 a < b, 返回 1 */
void compute(char c);
/* 从操作数栈里取出两数进行 c 运算,并把结果入栈*/
int getOdr(char c); //获取操作符在table中的下标;
public:
Solve();
void isolve();
~Solve();
void getNum(float c);
void getCh(char c);
float getRes();
};
五、运行结果
-
运行结果
-
测试结果:
六、个人总结
- 在本次结对编程中,我担任的角色是驾驶员,我的领航员是王安宁。虽然是第一次合作写代码,但是我们配合的很有默契,最后的完成了这个项目。这过程中我们也曾遇到过困难,但后来还是一起解决了。但这个项目我们完成的还不是很完美,由于我们之前都没有接触过图形化界面,也查找了很多资料,但出于时间考虑,我们主要实现前几个重要功能,并力争把代码效率质量提高。
- 对领航员的评价:安宁同学很好的扮演了领航员角色,在写代码之前她就已经为我准备了很多需要的资料,包括网上和书上的很多相关内容,为我们的后序编程提供了很大的帮助。在编程时,由于自己常常会粗心大意,漏写或写错一些字符符号,她总能很细心并且很及时的发现,并提醒我改正。正因如此,我们的代码质量大大提高,因为时间有限,这样的合作模式也提高了完成效率。。另外,安宁的单元自动测试代码也写得很好,为关键函数选用了合适的覆盖标准,针对程序中算符优先级的类和四则运算的结果计算进行了测试。
- 通过这次结对编程,我也发现了自身的很多不足之处。比如比如算法的逻辑思维能力差,代码格式不够标准规范,导致队友难以理解等。在今后的学习中我会更加努力的规范自己,进一步提升自己的变成水平。