结对项目
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34 |
---|---|
这个作业要求在哪 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230 |
这个作业的目标 | 设计一个四则运算生成器 |
成员:
3班3122004861:方尔博
4班3122004610:黄文超
github:hwc2459604249/AutoOperations (github.com)
*PSP2.1* | *Personal Software Process Stages* | *预估耗时(分钟)* | *实际耗时(分钟)* |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 500 | |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 100 | 60 |
· Design Spec | · 生成设计文档 | ||
· Design Review | · 设计复审 (和同事审核设计文档) | 140 | 140 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | ||
· Design | · 具体设计 | 30 | 70 |
· Coding | · 具体编码 | ||
· Code Review | · 代码复审 | 10 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 50 | 120 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 20 | 60 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | ||
合计 |
使用说明
-e
命令行参数按题目要求,只需要在指定参数后面跟合适的值即可
顺序不重要,可同时执行
性能分析
这里pd是计算答案的正确与错误数量,选取的数据量是10000,相当于重新计算了一遍get()里面部分内容
且各结构部分时间分配的也比较均衡
设计思路
基于字符串的切割
生成表达式
使用随机数得到需要的运算符个数n
随机判断加不加括号
使用a和b分别代替真分数和自然数
经过分析发现可使用字符数组来生成一个包含a,b的表达式,数组长度为4n+1
遍历字符数组
当i可被4整除时,表示这是数据,i+2能被数据整除时,放的是运算符,剩下的就是空格
考虑生成括号的情况,左括号只可能在数据的左边出现,而且最右边没必要出现,这里简单起见,随机生成一个子表达式的括号
恰可利用空格的分隔符,使用正则表达式切割字符串得到各个数据,从而找到各索引
随机生成真分数,将a替换真分数
用队列对先前生成的自然数保存起来,后面再替换b
计算
为了子表达式不出现负数,即包装一个计算子表达式的方法,即简单的加减乘除,而只有减法会可能出现负数,一旦出现了就返回null,后面调用它的方法从而可以判断出出现负数,于是重新生成表达式
整个表达式计算,首先判断有无括号,即先去括号化,将它先算出结果并替换括号里的内容
使用两个栈,一个存数据,另一个存运算符
先将最左边的数据放入栈中
使用len记录将要访问第几个数据
对字符串进行切割获得数据,然后字符串转成字符数组,遍历以找到每一个运算符
做如下判断
- 运算符栈为空 入栈(数据和运算符)
- **栈首元素为加减,而当前遍历到的运算符是乘除 ** 即优先级更高,入栈(数据和运算符)
- 以上情况都不是 将数据栈中第二个数据为首,栈首为尾,将运算符栈首弹出作为运算符,拼接为字符串传入前面的子表达式计算中,然后将当前运算符重复上述判断
当运算符栈不为空,就重复第三种情况的步骤,此时的计算顺序已经确定好了
Stack<String> numStack = new Stack<>();
Stack<Character> opeStack = new Stack<>();
char[] charArray = ans.toCharArray();
//获取数据
String[] split = ans.split(" . ");
int len = 0;
numStack.push(split[len++]);
for (int i = 0; i < charArray.length; i++) {
if (charArray[i] == '+' || charArray[i] == '-' || charArray[i] == '÷' || charArray[i] == '×') {
if (opeStack.isEmpty()) {//运算符为空
numStack.push(split[len++]);
opeStack.push(charArray[i]);
} else {
char peek = opeStack.peek();
if ((peek == '+' || peek == '-') && (charArray[i] == '÷' || charArray[i] == '×')) {
//优先级更高,加入
opeStack.push(charArray[i]);
numStack.push(split[len++]);
} else {//当前运算符优先级小于栈首
String s = numStack.get(numStack.size() - 2) + " " + opeStack.pop() + " " + numStack.pop();
numStack.pop();
String compute = compute(s);
if (compute == null) return null;
numStack.push(compute);
i--;
}
}
}
}
while (!opeStack.isEmpty()) {
String beCount = numStack.pop();
String mainCount = numStack.pop();
ans = compute(mainCount + " " + opeStack.pop() + " " + beCount);
if(ans == null) return null;
numStack.push(ans);
// System.out.println("ans: " + ans);
}
接下来是对结果的简化
使用辗转相除法计算分子分母的最大公倍数
以及带分数的表示,这里只需要注意一些细节问题
给定文件判断答案正确与否
使用文件相关操作即可
读取题目每一行切割得到表达式,并计算答案
采用将答案间浮点化比较两者差值,小于0.01则认为答案正确
使用List集合保存成功和失败的序号
只是答案的格式多样,与浮点数的转化需要多条件判断
测试
随机生成了10000道题目
答案:
而且多次生成10000道题目,从中随机抽取一些题目并计算答案来比较,正确
以生成的文件为测试,发现也与预期一致
项目小结
黄文超:
这对于我的脑细胞是个很大的挑战,这几天看bug看得头晕眼花,顺便复习了文件操作以及对字符串操作的知识,
不过这次设计的比较简单,甚至没有使用面对对象,本来还考虑使用后缀表达式的(比较之前课设我就写了这个)
不过时间较为久远(我不会了),而我也不想重构了,本来还想着多种可能的实现的,但实现了这么一种就感觉精力有限了
前期的设计还是很重要的
方尔博:
这是一次有难度的作业,也是我第一次与别人合作一起写项目,
与队友及时的沟通同时分享自己的思考是个很有趣的过程,
因为在不同的看法中更能看到自己的不足之处,从而提升自己,对项目需求的实现也让我学到了很多东西,
黄文超同学严谨的思维方式和优秀的代码能力让我佩服
标签:numStack,结对,项目,运算符,charArray,ans,opeStack,表达式 From: https://www.cnblogs.com/ChaoBirds/p/18438528