一、项目介绍
首先附上项目的GitHub地址:
C++版本:https://github.com/Nevermore5421/PairingProjectCalculator
C#带界面版本:https://github.com/Nevermore5421/PairingProjectCalculatorWithGUI
拿到题目后,我和组队的同学经过讨论后,决定选择第二个项目——四则运算题目生成项目,在项目第三阶段中,我们选择的是第一个扩展方向,即将程序扩展为Windows电脑图形界面程序。然后我们对该项目的需求进行了分析:
第一阶段:
(1)生成一千道不重复的四则运算题目
(2)能解最多10个运算符的,带括号的四则运算表达式题目,并给出解
(3)支持真分数运算
(4)能接收用户输入,并给出答案对错的反馈
第二阶段:
支持乘方运算,以“^”和“**”两种方式都可表示乘方。
第三阶段:
(1)扩展程序,制作出Windows电脑图形界面程序
(2)增加倒计时功能,判断用户是否能在20秒内按时完成,并设置用户得分
(3)增加历史记录,把用户做题的成绩记录并展示
1.PSP表格预估时间
PSP表格实际时间将在博文结尾处写出
PSP2.1 |
Personal Software Process Stages |
预估耗时(min) |
实际耗时(min) |
Planning |
计划 |
60 |
|
Estimate |
估计这个任务需要多少时间 |
2000 |
|
Development |
开发 |
1200 |
|
Analysis |
需求分析(包括学习新技术) |
480 |
|
Design Spec |
生成设计文档 |
60 |
|
Design Review |
设计复审(和同事审核设计文档) |
20 |
|
Coding Standard |
代码规范(为目前的开发制定合适的规范) |
20 |
|
Design |
具体设计 |
200 |
|
Coding |
具体编码 |
400 |
|
Code Review |
代码复审 |
60 |
|
Test |
测试(自我测试,修改代码,提交修改) |
200 |
|
Reporting |
报告 |
30 |
|
Test Report |
测试报告 |
20 |
|
Size Measurement |
计算工作量 |
10 |
|
Postmortem & Process Improvement Plan |
事后总结,并提出过程改进计划 |
60 |
|
|
合计 |
2000 |
|
2.解题思路描述
由于此项目时结对项目,我和合作的队友进行了分工,最终决定我负责生成题目部分,以及图形界面部分;队友负责解运算表达式部分。故在此篇介绍中我主要介绍生成题目部分的思路。
1)生成运算题目思路
刚开始思考这个问题时,发现生成一个四则运算题目本身不是一件特别难的事情,主要就有以下三个部分:
- 生成随机数字
- 生成加减乘除,乘方的算术符号
- 生成左右括号
但实际编写代码的过程中,会遇到很多细节上的问题,并没有想象中那么简单,例如:
- 生成表达式的长度要进行随机化和合理化,不能太简单也不能太难,更不能每道题都一样
- 某种特定运算符的运算数要进行合理化,除号后不能放0,乘方后不能放太大的数,不然正常人无法计算
- 运算符的随机生成过程同样要考虑合理化,不能出现连续多个乘方等难以计算的式子
- 括号的生成也需要合理化,例如除号后不能出现一个括号中算出是0的情况,也不能出现一个乘方符号后的数字虽然很小,但是带了括号,最终也变得很大,人类无法计算的情况出现
- 对真分数的计算,不能用正常方法计算
为解决上述问题,我针对每个问题进行了特殊处理:
- 对于表达式长度,我选择了用C11新标准中添加的random头文件中自带的正态分布来进行长度控制,使得大部分表达式长度在5左右,难度对大部分人比较合适,也会有几道比较难的和比较简单的题目,具体实现过程会在下一个部分中讲述
- 对于运算符和运算数合理化,我将乘方后的数(即幂数)控制在小于等于3,符合一般人的计算水平,同时我控制每个表达式中的乘方数量尽量少,同时不允许连续的乘方出现
- 对于括号的生成,我进行特殊判定,使得除号和乘方后不允许放左括号,这样也就不会出现上述问题了
- 对于真分数计算,我们写了一个类,把所有的数字(无论是否是真分数)都用分数表示,类中有分子,分母属性,同时将加减乘除和乘方符号重载,能直接计算这个类的对象
2)解运算题目思路
解四则运算式的方法并不难,基本思路将中缀表达式转为后缀表达式计算,即将运算符与运算数分别储存在两个栈中,按照一定的规则进栈与退栈。规则包括:
- 数字直接压入运算数栈
- 左括号直接压入运算符栈
- 右括号式运算符栈持续弹出直到弹出左括号为止
- 定义运算符优先级 +,- <*,/<^,**,当遇到运算符时,将使运算符栈持续弹出,直到栈顶运算符优先级小于当前运算符或者左括号以及栈空情况,在将运算符压入栈
- 每弹出一个运算符,弹出两个运算数,进行计算后再压入栈
- 当算式处理完,使运算符栈不断弹出,直至栈为空,则运算数栈栈顶即为结果
但单纯处理只能处理浮点数范围情况,由于需要支持分数情况,我们重新设计一个类,勒种包括分子,分母等分数的特征,每一数字x在一开始就化为分数x/1的情况,再将分数的特征量,记录下来,并重载+,-,*,/,^运算符,使其按照分数的规则计算。
设计完这个类后只需按照上述规则进行正常