20162311 结对编程项目-四则运算 阶段总结
一、需求分析
- 能随机生成n道题目,n由使用者输入
- 支持整数的四则运算
- 能够判断正误,错误时能提醒并输出正确答案
- 能计算出正确率
- 能多次生成题目,直到使用者选择退出
后续拓展的可能
- 支持真分数
- 题目去重
- 支持多运算符
备注:这次只实现了单运算符,而且做整数除法时只能得到整数结果;如
8 / 7
结果为1。这周还没有解决这些问题,下周会继续学习并解决。
二、设计思路
Operand
类用来生成两个10以内的随机数,作为操作数。设计一个IntQuestions
类,其中实例化一个Operand对象,用来生成操作数,还包含四种方法,分别生成两个10以内随机整数的加、减、乘、除,这四种方法返回值都为String类型。还有一个Judgement
类,用来判断输入的答案是否正确。NiToSuffix
类是用来将中缀表达式转化为后缀表达式,运用了栈的方法。把生成的后缀表达式作为参数传入Judgement
类中的evaluate()
方法,从而判断结果是否正确。ResultTest
是结果测试类,它依赖于之前的类,用于生成表达式,输出结果是否正确,输出正确率。
UML类图
三、实现过程中的关键代码
- 中缀表达式转化为后缀表达式
package Arithmetic;
import java.util.*;
/**
* Created by Administrator on 2017/5/13.
*/
public class NifixToSuffix {
private Stack<String> stack;
private List<String> list;
private String message,Message="";
public NifixToSuffix() {
stack = new Stack<String>();
list = new ArrayList<String>();
}
public void conversion(String expr) {
int op1, op2, result = 0;
String token;
StringTokenizer tokenizer = new StringTokenizer(expr);
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken();
if (token.equals("("))
stack.push(token);
else if (token.equals("+") || token.equals("-")) {
if(!stack.empty()) {
if (stack.peek().equals("(")){
stack.push(token);
}
else if (stack.peek().equals("*") || stack.peek().equals("/"))
list.add(stack.pop());
}
else stack.push(token);
}
else if (token.equals("*") || token.equals("/")) {
if(!stack.empty()){
if(stack.peek().equals("*")||stack.peek().equals("/")) {
list.add(stack.pop());
}
}
stack.push(token);
}
else if (token.equals(")")) {
while (true) {
String A = stack.pop();
if (!A.equals( "("))
list.add(A);
else break;
}
}
else list.add(token);
}
while (!stack.empty()) {
list.add(stack.pop());
}
ListIterator<String > li = list.listIterator();
while (li.hasNext()) {
Message += li.next() + " ";
li.remove();
}
message = Message;
}
public String getMessage() {
return message;
}
}
转换规则
- 设立一个栈和一个列表,栈存放运算符,列表存放操作数,栈和列表都为空。
- 编译程序从左到右扫描原表达式,若遇到操作数,进入列表。
- 若遇到运算符,与栈顶进行比较,比栈顶级别高则进栈,否则退出栈顶元素,将其加到列表中。
- 若遇到左括号,进栈;若遇到右括号,则一直退栈输出到列表中,退到左括号为止。
- 当栈变为空时,用迭代器将列表中的元素取出,每取出一个,都加一个空格作为分隔符,最后得到后缀表达式
迭代代码如下
ListIterator<String > li = list.listIterator();
while (li.hasNext()) {
Message += li.next() + " ";
li.remove();
}
四、测试方法
-
NifixToSuffix
类的方法的测试
-
Judgement
类的方法测试evaluate()
方法
isOperator()
方法
五、运行过程截图
六、代码托管地址
七、遇到的困难及解决方法
- 问题1
如何计算表达式的值?
- 解决方法
老师之前布置过一个课堂练习,让我们计算后缀表达式的值。老师把代码框架给了我们,教我们用栈进行计算。我将之前的那部分代码复制过来,新建一个
Judgement
类,把实现计算过程的代码作为判断的方法,通过获取返回值与用户输入的答案进行比较
package Arithmetic;
/**
* Created by Administrator on 2017/5/13.
*/
import java.util.StringTokenizer;
import java.util.Stack;
public class Judgement
{
/* constant for addition symbol */
private final char ADD = '+';
/* constant for subtraction symbol */
private final char SUBTRACT = '-';
/* constant for multiplication symbol */
private final char MULTIPLY = '*';
/* constant for division symbol */
private final char DIVIDE = '/';
/* the stack */
private Stack<Integer> stack;
public Judgement() {
stack = new Stack<Integer>();
}
public int evaluate (String expr)
{
int op1, op2, result = 0;
String token;
StringTokenizer tokenizer = new StringTokenizer (expr);
while (tokenizer.hasMoreTokens())
{
token = tokenizer.nextToken();
//如果是运算符,调用isOperator
if (isOperator(token))
{
//从栈中弹出操作数2
op2 = stack.pop();
//从栈中弹出操作数1
op1 = stack.pop();
//根据运算符和两个操作数调用evalSingleOp计算result;
result = evalSingleOp(token.toCharArray()[0], op1, op2);
//计算result入栈;
result = stack.push(result);
}
else //如果是操作数
stack.push(Integer.parseInt(token));
//操作数入栈;
}
return result;
}
public boolean isOperator (String token)
{
return ( token.equals("+") || token.equals("-") ||
token.equals("*") || token.equals("/") );
}
private int evalSingleOp (char operation, int op1, int op2)
{
int result = 0;
switch (operation)
{
case ADD:
result = op1 + op2;
break;
case SUBTRACT:
result = op1 - op2;
break;
case MULTIPLY:
result = op1 * op2;
break;
case DIVIDE:
result = op1 / op2;
}
return result;
}
}
- 问题2
IntQuestions
类生成的是中缀表达式,如何转化为后缀表达式呢?
- 解决方法
也是运用栈。老师上课时也讲过,但是不清楚具体怎么实施。问了学的比较好的张旭升同学,看了一下他的代码。代码截图在上面有,具体操作在实现过程中的关键代码这点里面讲了,这里就不重复了。
八、对结对的小伙伴的评价
-
结对搭档:20162325金立清博客
-
评价
与其说是结对编程,不如说是一个人编
(搭档不给力)。坦白说,金立清同学之前学的不是很好,作为搭档,我也有责任,我没有起到监督作用。结对编程时基本上是我编,金立清同时也学习。金立清同学在经过我的讲解后,可以看懂代码,但是不会自己编。所以我觉得她最需要改进的就是把之前学的知识恶补一下,这样在以后的结对编程中才能有自己的想法,而不是只能看着我编,然后再讲解给她听。不过值得肯定的是她能认识到自己的不足,虚心学习,现在还来得及。
九、参考或引用的设计、实现
- 后缀表达式求值
参考娄老师的
mini dc
课堂测试,这里是题目链接
while (tokenizer.hasMoreTokens())
{
token = tokenizer.nextToken();
//如果是运算符,调用isOperator
if (isOperator(token))
{
//从栈中弹出操作数2
op2 = stack.pop();
//从栈中弹出操作数1
op1 = stack.pop();
//根据运算符和两个操作数调用evalSingleOp计算result;
result = evalSingleOp(token.toCharArray()[0], op1, op2);
//计算result入栈;
result = stack.push(result);
}
else //如果是操作数
stack.push(Integer.parseInt(token));
//操作数入栈;
}
这段代码老师只给了中文注释,是自己实现的,其余的框架是老师写好的
- 中缀表达式转化成后缀表达式
参考娄老师的栈的应用PPT,学习了转化的思路;还请教了张旭升同学,并参考引用了他的代码
十、PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 20 | 30 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 90 | 120 |
· Design Spec | · 生成设计文档 | 120 | 140 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 25 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 120 | 150 |
· Coding | · 具体编码 | 300 | 400 |
· Code Review | · 代码复审 | 60 | 50 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 90 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 90 | 120 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 980 | 1215 |
注:以上耗时均为本周耗时,下周完成后会有变动
本周的预计和实际耗时相差较大,主要是没有认识到任务的难度(高估了自己,哎),对Java的一些类的运用还不够熟练,所以在问同学,查资料上花了很多时间,不过这也促进了我的学习,希望下周能合理预估时间,同时学习到更多。