结对编程Core14组
结对编程Core14组
沈三景
姜文彬
结对编程
Github地址
https://github.com/ToumaKazusa3/homework2.git
项目介绍
- 能自动生成小学四则运算题目并给出答案,生成题目时可以选择下列参数:
1)生成题目数量
2)每道题目中运算数数量
3)运算数数值范围
4)运算符种类( + - * / )
5)运算数类型(整数,小数,分数)
6)运算数精度(保留到小数点后0~4位) - 将四则运算的计算功能包装在一个模块中
- 将Core模块通过一定的API 接口和其他组的模块( UI )交流
- UI组测试所有 Core 组 DLL 后,Core 组根据 UI 组的测试结果进行反馈优化
功能分析:
这次的程序由于需要产生有括号和没有括号的,而这两种算式计算的程序有所差别,所以写了两个函数,分别产生有括号和没有括号的算式,有括号的在产生的同时进行计算并根据计算结果来进行调整,没有括号的在产生时只根据是否需要整除来对除法的操作数进行调整,计算用另外的函数实现。
在产生算式时完全用随机数来产生所有的东西,包括操作数、运算符、运算符个数以及新产生的操作数是放到现有算式的左边或者右边,根据运算符优先级和是否需要括号来判断是否添加括号,根据是否需要整除来对操作数进行调整。最后把生成的操作数和运算符连接到一起生成最终的算式,而有括号的算式结果也在程序运行过程中得到,没有括号的算式结果用另外的函数计算。
计算函数采用数据结构堆(stack)的思想,具体的算法如下:
具体代码如下:
bool isoptr(string ch)
{
if (ch == "+" || ch == "-" || ch == "*" || ch == "/" || ch == "(" || ch == ")" || ch == "#")
{
return true;
}
return false;
}
int cmp(string ch1, string ch2)
{
if (ch1 == "+" || ch1 == "-")
{
if (ch2 == "+" || ch2 == "-" || ch2 == ")" || ch2 == "#")
{
return 0;
}
else
{
return 1;
}
}
if (ch1 == "*" || ch1 == "/")
{
if (ch2 == "(")
{
return 1;
}
else
{
return 0;
}
}
if (ch1 == "(")
{
if (ch2 == ")")
{
return 3;
}
else
{
return 1;
}
}
if (ch1 == ")")
{
return 0;
}
if (ch1 == "#")
{
if (ch2 == "#")
{
return 3;
}
else
{
return 1;
}
}
}
int splitfind(string str, int &i)
{
//find split
//int j = i;
while (str[i] != '+' && str[i] != '-' && str[i] != '*' && str[i] != '/' && str[i] != '=')
{
i++;
}
/*
if (j == i)
{
return 0;
}
else
{
return 1;
}
*/
return 0;
}
int splitstr(vector<string> &vstr, string str)
{
vstr.push_back("#");
int i = 0, j = 0;
string s;
while (1)
{
splitfind(str, i);
if (str[i] == '=')
{
break;
}
s = str.substr(j, i - j);//num
vstr.push_back(s);
s = str.substr(i, 1);//op
vstr.push_back(s);
i++;
j = i;
}
s = str.substr(j, i - j);
vstr.push_back(s);
vstr.push_back("#");
return 0;
}
int stringtodouble(string s, double &num)
{
stringstream ss;
ss << s;
ss >> num;
return 0;
}
int doubletostring(string &s, double num)
{
stringstream ss;
ss << num;
s = ss.str();
return 0;
}
int calc(vector<string> &vs, vector<string> &vscal)
{
string str;
vector<string> vstr;
stack<string> OPND;
stack<string> OPTR;
for (int q = 0; q < vs.size(); q++)
{
str = vs[q];
splitstr(vstr, str);
OPTR.push("#");
int i = 1;
int k = 0;
int sumsub = 0;
int foundsub = 0;
string op;
string a, b, c;
double doublea, doubleb, doublec;
map<int, int> order1, order2;
for (int j = 0; j < vstr.size(); j++)
{
order1[j] = j;
order2[j] = j;
}
while (1)
{
if (!isoptr(vstr[i]))
{
OPND.push(vstr[i]);
i++;
continue;
}
else
{
while (1)
{
op = OPTR.top();
switch (cmp(op, vstr[i]))
{
case 0:
b = OPND.top();
OPND.pop();
a = OPND.top();
OPND.pop();
OPTR.pop();
stringtodouble(b, doubleb);
stringtodouble(a, doublea);
if (op == "+")
{
doublec = doublea + doubleb;
doubletostring(c, doublec);
OPND.push(c);
}
if (op == "-")
{
sumsub++;
doublec = doublea - doubleb;
if (doublec < 0)
{
doublec = doubleb - doublea;
for (k = 0; k < vstr.size(); k++)
{
if (vstr[k] == "-")
{
foundsub++;
}
if (foundsub == sumsub)
{
break;
}
}
int k1 = k, k2 = k;
k1--;
k2++;
while (vstr[k1] != "+" && vstr[k1] != "-" && vstr[k1] != "#")
{
k1--;
}
while (vstr[k2] != "+" && vstr[k2] != "-" && vstr[k2] != "#")
{
k2++;
}
int s;
for (s = 1; s < k2 - k; s++)
{
order2[k1 + s] = order1[k + s];
}
order2[k1 + s] = k;
int t = k1 + s;
for (s = 1; s < k - k1; s++)
{
order2[t + s] = order1[k1 + s];
}
doubletostring(c, doublec);
OPND.push(c);
}
else
{
doubletostring(c, doublec);
OPND.push(c);
}
}
if (op == "*")
{
doublec = doublea * doubleb;
doubletostring(c, doublec);
OPND.push(c);
}
if (op == "/")
{
doublec = doublea / doubleb;
doubletostring(c, doublec);
OPND.push(c);
}
break;
case 1:
OPTR.push(vstr[i]);
break;
case 2:
OPTR.pop();
break;
default:
break;
}
if (cmp(op, vstr[i]) != 0)
{
break;
}
}
}
if (vstr[i] == "#" && OPTR.top() == "#")
{
break;
}
i++;
}
string ans;
ans = OPND.top();
string anscal = "";
for (int i = 1; i < vstr.size() - 1; i++)
{
// cout << vstr[order2[i]];
anscal += vstr[order2[i]];
}
anscal += "=";
// cout << "=";
anscal += ans;
// cout << ans;
vscal.push_back(anscal);
vstr.clear();
while (!OPND.empty())
{
OPND.pop();
}
while (!OPTR.empty())
{
OPTR.pop();
}
}
return 0;
}
封装
在按照常规套路进行一番操作之后发现并不能封装成DLL,编译器报错说找不到对应的头文件,但是头文件和相关的设置已经做好了,忙了一段时间之后还是不能解决,于是弃疗直接用头文件加上命名空间来与UI组对接,在对接过程中也出现了许多问题,比如:IDE不能识别加进去的头文件,解决办法:在IDE里添加新的空文件,然后将代码复制过去。(至今没有明白这是为啥)
测试结果
-
没有括号并且整除:
-
没有括号不整除:
-
有括号并且整除:
-
有括号不整除:
-
以及分数运算:
(在这里我们没有区分分数与除法)
从产生的结果来看符合预期的功能
结对编程的意义
结对编程主要是可以分担任务,一个人的任务不会太重,并且互相监督可以加快开发的进度,也可以避免许多手误以及不注意的bug,两个人合作可以通过交流来使问题更好的被解决,虽然是很可能有分歧的,但是可以通过不断的探讨来消除分歧。在debug时两个人也可以更快的发现问题所在,尽快解决,大脑也不会因为负担过重而死机,总之,结对编程确实可以提高效率。
PSP表格