2019软件工程结对项目-1120172176
项目设计
1. 项目成员及Github项目地址
项目成员 | 博客地址 | github地址 |
---|---|---|
陆俊奇 | https://blog.****.net/SaltyFishLu/article/details/104034857 | https://github.com/SaltyFishL/AutoCal |
吴大伟 | https://me.****.net/qq_40026187 | https://github.com/AmnesiaWu/AutoCal |
2.预计时间表格
PSP2.1 | Personal Software Process Stages | 预估耗时 (分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | |
Estimate | 估计这个任务需要多少时间 | 30 | |
Development | 开发 | 1930 | |
Analysis | 需求分析(包括学习新技术) | 90 | |
Design Spec | 生成设计文档 | 60 | |
Design Review | 设计复审(和同事审核设计文档) | 30 | |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 30 | |
Design | 具体设计 | 120 | |
Coding | 具体编码 | 1200 | |
Code Review | 代码复审 | 60 | |
Test | 测试(自我测试,修改代码,提交修改) | 240 | |
Reporting | 报告 | 190 | |
Test Report | 测试报告 | 120 | |
Size Measurement | 计算工作量 | 10 | |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 60 | |
Total | 合计 | 2150 |
3. 解题思路描述
3.1 项目要求
3.1.1 第一阶段
- 写一个能自动生成小学四则运算题目的命令行“软件”
- 用-i num实现一次性生成的题目个数
- 实现对一个最多10个运算符表达式的求值
- 支持真分数的四则运算
- 实现判定对错,并最后给出总共对/错数量。
3.1.2 第二阶段
- 支持乘方运算,命令行参数-m 1为^表示乘方,命令行参数-m 2为**表示乘方。
3.1.3 第三阶段
- 把程序编程一个Windows上的电脑图形界面程序
- 增加倒计时功能,每道题必须要20s内完成,否则得0分并开始下一题
- 增加历史记录功能,把用户做题的成绩记录下来并可以展现历史记录
3.2 思考过程
题目主要有几个难点:
- 生成不同的表达式
- 求解表达式
- 命令行参数的设置
- 图形界面,倒计时和历史记录
3.2.1 生成表达式
需要生成随机数,随机运算符,随机括号,还要保证表达式的正确性和唯一性。
生成表达式的时候,感觉还是中缀表达式最直观,最简单。而且不管怎样,最后呈现在用户面前的肯定也要是中缀表达式。当然,为了便于计算表达式,我们制定了了以下生成表达式时的规则:
- 一个表达式中最多只有一个乘方运算
- 除号和幂运算后不生成左括号,防止括号内的值为0
- 乘方幂的范围为1,2,3
- 生成左括号的概率为1/5
- 在有左括号的情况下,生成右括号的概率为1/3,且配对的右括号和左括号之间至少隔一个运算符
为了消除括号,也为了求解表达式方便,程序首先将中缀表达式转换成了后缀表达式。
最后就是要为表达式去重,所采用的数据结构是二叉树。每一个算式对应一个二叉树,所有的叶子节点为数值,所有的非叶节点为符号。有限次交换加号和乘号左右的算术表达式即所有二叉树的+和根节点的左右子节点任意交换次数不能得到相同的二叉树结构。
3.2.2 求解表达式
从二叉树底向上求解,体现在代码中就是从根节点递归求解。
3.2.3 图形界面,倒计时,历史记录
图形界面最终用的是tkinter库。倒计时和历史记录未实现。
4. 设计实现过程
4.1 类模块说明
4.1.1 Num类
不管用户选择的是什么模式,我们都用Num类来存放数字。因为即使是整数,也可以用分母为1的分数来表示。
- int numerator //分子
- int denominator //分母
- int sign //符号,正数为1,负数为-1
- int gcd //分子分母的最大公约数,在化简的时候可以用到
方法:
- +, -, *, /, **, ==, <, str()运算符的重载
- __gcd() //求解分子分母的最大公因数
- __reduction() //化简操作,保证分子分母均为正数,符号由sign来定
4.1.2 ExpressionBuilder类
属性:
- int[] exp_elements_infix //中缀表达式数组,每个符号数字都对应其中一项
- int[] exp_elements_suffix //后缀表达式数组,去除了括号
- BinaryTree tree //二叉树形式
- Num ans //答案
方法:
- __rand_op_num() //随机出1到10的随机数个数
- __rand_op() //随机出一个运算符
- __rand_num() //随机出一个0到100的数字
- __rand_pow_num() //随机出一个1到3的数字作为幂
- build_exp_infix() //生成中缀表达式
- infix_to_sufix() //将中缀表达式转成后缀表达式
- suffix_to_tree() //将后缀表达式转成树
- get_ans() //求解表达式
- build() //留给外部调用的函数,依次调用上述提到的函数
4.1.3 BinaryTree类
内部类:Node
python里没有内部类的概念,这里仅是这么叫而已。二叉树的节点,type代表节点是数字还是字符(是否为叶节点),如果是非叶节点,op为操作数;如果是叶节点,num为数字,left表示左孩子,right表示右孩子。
属性:
- root //表达式树的根节点
- op_num //表达式中运算符的个数
方法:
- adjust_tree() //在计算完之后对数进行调整,以便后生成的表达式树预期对比看是否相同
- is_same_tree() //判断两颗表达式树是否相同
4.1.4 Const
专门存放常数的类,增强代码的可读性。
4.1.5 Stack
python没有现成的stack,写了一个简易版的,基本功能都有。
4.1.6 Main
解析输入参数,调用其他类,实现与用户的交互
4.2 程序执行流程图
5. 单元测试
- 单元测试用例设计如下
1.输入测试
主要测试程序的合法输入以及不合法输入情况。
编号 | 输入格式 | 预期输出 |
---|---|---|
1 | 简单模式 + max_num | 整数运算式 |
2 | 中等模式 + max_num | 分数、整数运算式 |
3 | 困难模式 + max_num | 分数、整数乘方运算式 |
2.输入测试
检测四则运算器中每一种算符的运算正确性
编号 | 操作数1 | 操作数2 | 运算符 | 预测结果 |
---|---|---|---|---|
1 | 2 | 1/2 | + | 5/2 |
2 | 2 | 1/2 | - | 3/2 |
3 | 2 | 3/2 | * | 3 |
4 | 2 | 1/2 | / | 4 |
5 | 3 | 3 | ^ | 27 |
6 | 3 | 0 | ^ | 1 |
7 | 1/2 | 1 | ^ | 1/2 |
3.题目查重测试
编号 | 算式1 | 算式2 | 预测结果 |
---|---|---|---|
1 | 2+3+4 | 4+(2+3) | 重复 |
2 | 1+2+3 | 3+2+1 | 不重复 |
3 | 2*3 | 3*2 | 重复 |
4 | (1 + 2) * 3 | 3* (1 + 2) | 不重复 |
5 | (1+2)*(3+4) | (3+4)*(1+2) | 不重复 |
6 | (2-1)/(5-3) | (5-3)/(2-1) | 不重复 |
7 | (3+6)/(5-3) | (6+3)/(5-3) | 重复 |
8 | (1/2+2/3)+3/4 | 1/2+(2/3+3/4) | 重复 |
9 | (1/2+2/3)* 3/4 | 3/4 *(1/2+2/3) | 重复 |
6.性能分析
由分析报告可见,该项目的Gui.py里的main函数占用时间最多。build函数是生成运算表达式的函数,可从图看出调用了1000次(即生成了1000个算式),时间花了总时间的2.1%,可见主要时间花在与用户的交互上
7.程序扩展
我们用python做了个简单图形界面
主界面如下:
输入格式错误:
开始答题:
回答错误:
退出并显示分数界面:
点击确定后程序结束
8.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时 (分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 40 |
Estimate | 估计这个任务需要多少时间 | 30 | 40 |
Development | 开发 | 1930 | 1350 |
Analysis | 需求分析(包括学习新技术) | 90 | 60 |
Design Spec | 生成设计文档 | 60 | 50 |
Design Review | 设计复审(和同事审核设计文档) | 30 | 60 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 30 | 10 |
Design | 具体设计 | 120 | 60 |
Coding | 具体编码 | 1200 | 900 |
Code Review | 代码复审 | 60 | 40 |
Test | 测试(自我测试,修改代码,提交修改) | 240 | 60 |
Reporting | 报告 | 190 | 150 |
Test Report | 测试报告 | 120 | 60 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 60 | 10 |
Total | 合计 | 2150 | 1520 |
9.总结
感觉部分需求有点挺没必要的,比如生成的表达式不重复。毕竟之前的数字是随机的,符号是随机的,括号是随机的,符号的数目也是随机的,这所有的随机下来,重复的可能性就非常非常低了。如果是真实的开发中,我觉得耗费大量的时间和精力在这个不影响结果正确性且概率极低的东西上是没有必要的,也不符合软件工程的目标。其他感觉还好,毕竟花的时间也不多,做的效果也基本满足需求了。