结对作业

这个作业属于哪个课程 软件工程
这个作业要求在哪里 作业要求
这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序
队伍成员 3118005361郭裕霖3118005379丘文龙
github https://github.com/chiguozi666/primerschool

PSP表格


PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 40 45
· Estimate · 估计这个任务需要多少时间 15 15
Development 开发 700 720
· Analysis · 需求分析 (包括学习新技术) 60 50
· Design Spec · 生成设计文档 15 20
· Design Review · 设计复审 (和同事审核设计文档) 20 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 15
· Design · 具体设计 40 60
· Coding · 具体编码 500 480
· Code Review · 代码复审 50 45
· Test · 测试(自我测试,修改代码,提交修改) 150 120
Reporting 报告 70 120
· Test Report · 测试报告 50 30
· Size Measurement · 计算工作量 30 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 10
合计 合计 1760 1770

代码具体实现

流程图

表达式的生成

/**
     * 生成表达式
     * @param range
     * @return (一个带分数的表达式;一个假分数的表达式)方便计算;
     */
    public static String createExp(int range) {//生成表达式
        String str = "";
        String str1 = "";
        do {
            str = "";//标准的式子
            str1 = "";//假分数的式子
            int t = 0;//算式中的数字个数
            Random rand = new Random();
            t = rand.nextInt(3) + 2;
            String[] number = new String[t];//存放数字
            String[] symbol = new String[t - 1];//存放运算符号,有t个数字就应该有t-1个符号
            String[] total = new String[4 * t - 3];//存放式子
            String[] total1 = new String[4 * t - 3];
            for (int i = 0; i < t; i++) {
                number[i] = createMath(range);//生成整数或者分数,range是范围
            }
            for (int i = 0; i < t - 1; i++) {
                symbol[i] = createOp();//随机生成运算符号
            }
            //数字+空格+操作符+空格+数字刚好是模4操作
            for (int i = 0; i < 4 * t - 3; i++) {
                if (i % 4 == 0) {
                    total[i] = number[i / 4];
                    total1[i] = number[i / 4];
                } else if (i % 4 == 2) {
                    int k = (i + 2) / 4 - 1;
                    total[i] = symbol[k];
                    total1[i] = symbol[k];
                } else {
                    total[i] = " ";
                    total1[i] = " ";
                }
            }
            //total1把里面的真分数都转成假分数输出方便算答案
            for (int i = 0; i < 4 * t - 3; i++) {
                if (total1[i].contains("'")) {
                    total1[i] = trueToFalse(total1[i]);
                }
            }
            //当t=4的时候就加括号就是了
            if (t == 4) {
                if ((symbol[1] == "*" || symbol[1] == "/") && (symbol[0] == "+" || symbol[0] == "-")) {
                    total[0] = "(" + total[0];
                    total[4] = total[4] + ")";
                    total1[0] = "(" + total1[0];
                    total1[4] = total1[4] + ")";
                }
                if ((symbol[1] == "*" || symbol[1] == "-") && (symbol[2] == "+" || symbol[2] == "-")) {
                    total[8] = "(" + total[8];
                    total[12] = total[12] + ")";
                    total1[8] = "(" + total1[8];
                    total1[12] = total1[12] + ")";
                }
            }
            //拼接total字符串
            for (int i = 0; i < 4 * t - 3; i++) {
                str = str + total[i];
                str1 = str1 + total1[i];
            }
            //判断答案是否为负数,假如是负数就重来
        } while (ExpCounter.count(str1).contains("-"));
        // System.out.println(str);
        return str + " =" + ";" + str1;
    }

creatMath()方法生成整数或者分数,creatOp()方法生成运算符号,分别放入两个数组当中。total数组是用来存放表达式的。
还没加括号前,因为生成的表达式是形如数字+空格+操作符+空格+数字(例1 + 2 * 3),第一个数字的序号是0,那么第一个数字的序号模4就为0,其余数字的序号模4也为0,也就是说total数组中模4为0的位置就是放数字的,符号的序号模4为2,即total数组中模4为2的位置放符号,其余都是空格。那么就可以通过for循环和if判断将数字和符号放入正确的位置,形成表达式。

后缀表达式的生成

检查重复和计算答案都需要先把表达式变为后缀表达式
实现后缀表达式的生成是由一个栈和一个存放表达式的字符串来完成。

public static String creatSuffixExepression(String str) {
		int num=0;
		int tmp=0;
		int stackLen=0;//栈的长度
		String expression="";//输出结果字符串
		char element='a';
		boolean haveNum= false;//是否有数字
		Stack<Character> stack= new Stack<>();//字符栈
		str=str+"!";//为了判断结束方便加入!做为字符串结尾
		for(int i=0;i<str.length();i++) {
			char c = str.charAt(i);
			if(isNUm(c)) {
				haveNum=true;
				tmp=tmp*10+(c-'0');
			}else {
				if(haveNum) {
					haveNum=false;
					expression+=tmp;
					tmp=0;
				}
				if(c=='(') {//遇到左括号无条件进栈
					num++;
					stack.push(c);
				}else if(c=='+'||c=='-'||c=='!') { //因为+-优先级最低所以全部弹出
					if(num>0) {//如果遇到左括号则停止
						stackLen= stack.size();
						for(int j=0;j<stackLen;j++) {
							element = stack.peek();
							if(element=='(') {
								break;
							}
							//change
							expression +=' ';
							expression+=stack.pop();
						}
					}else {//全部弹出;
						expression+=' ';
						if(stack.size()!=0) {
							stackLen = stack.size();
							for(int j = 0; j < stackLen; j++ ) {
								//change
								expression +=' ';
								expression += stack.pop();
							}
						}
					}
					//读到结尾了
					if(c=='!') {
						break;
					}
					stack.push(c);
					expression+=' ';
				}else if(c=='*'||c=='/') {
					if(str.charAt(i-1)==' '){
						while (!stack.empty() && priority(c) <= priority(stack.peek())){//弹出栈顶优先级大于当前元素的符号;
							expression +=' ';
							expression += stack.pop();
						}
						stack.push(c);
					}else {
						expression +=c;
					}
				}else if(c==')') {
					if(stack.size()!=0) {
						stackLen = stack.size();
						for(int j = 0; j < stackLen; j++ ) {
							element = stack.pop();
							if(element=='(') {
								break;
							}
							//change
							expression+=' ';
							expression += element;
						}
						num--;
					}
				}else if(c==' ') {//遇到空格则加空格;
					expression+=' ';
				}
			}
		}
		return expression;
	}

计算答案

public static String count(String str1) {
        //创建后缀表达式
        String str = ExpResolver.creatSuffixExepression(str1);
        char c = 'b';
        boolean haveNum = false;//是否有数字
        boolean haveFraction = false;//是否为分数
        int tmp = 0;//保存数字也充当分母
        int numer = 0;//分子
        int div = 0;//最大公约数
        Value a = null;
        Value b = null;
        Value curResult = null;

        Stack<Value> number = new Stack<>();
        for (int i = 0; i < str.length(); i++) {
            c = str.charAt(i);
            //把连续数字的缓存下来
            if (isNum(c)) {
                haveNum = true;
                tmp = tmp * 10 + (c - '0');
            } else {
                if (haveNum) {
                    haveNum = false;
                    if (isSpace(c)) {
                        if (haveFraction) {
                            Value xx = new Value(numer, tmp);
                            number.push(xx);//存分数进栈
                            tmp = 0;
                        } else {
                            Value xx = new Value(tmp, 1);
                            number.push(xx);//存整数进栈
                            tmp = 0;
                        }
                    } else {
                        haveFraction = true;
                        numer = tmp;
                        tmp = 0;
                        continue;
                    }
                }
                if (c == ' ') {
                    if (haveFraction) {
                        haveFraction = false;
                    }
                    continue;
                } else if (c == '+' || c == '-' || c == '*' || c == '/') {
                    b = number.pop();
                    a = number.pop();
                    switch (c) {
                        case '+':
                            curResult = add(a, b);
                            break;
                        case '-':
                            curResult = subtract(a, b);
                            break;
                        case '*':
                            curResult = mul(a, b);
                            break;
                        case '/':
                            curResult = division(a, b);
                            break;
                    }
                }
                number.push(curResult);
            }
        }
        if (number.size() > 1) {
            //throw new Exception("错误");
            System.out.println("错误"); //如果所有操作符都进行相应操作数后栈内还有多个元素,则说明出错
        }
        Value result = number.pop();
        if (result.up != 0) {               //若分子不为0,则对分子分母约分
            div = gcd(curResult.down, curResult.up);//最大公因子
            result.up /= div;
            result.down /= div;
        }
        // System.out.println(z.numerator+"/"+z.denominator);
        return ExpCounter.transform(result.up, result.down);
    }

计算答案需要用到栈和后缀表达式,比如后缀表达式1 2 + 3 *,遇到数字就进栈,遇到符号就出栈运算,在将结果进栈,最后就能得到结果。

检查重复


首先拿到一个中缀表达式,然后通过压栈的方式得到后缀表达式,拿到后缀表达式以后就可以轻松的创建一个二叉树,有了这个二叉树后,就可以用这个二叉树进行判断。
其中使用了一个静态变量来记录来维护变换的次数,保证只交换一次。这样就能判断重复。

运行结果

性能分析


成员 个人总结 队友闪光点
丘文龙 编程能力还需提升,多跟队友探讨思路 队友写的代码看着太舒服了,容易理解
郭裕霖 编程能力还需提升,多跟队友探讨思路 队友写的代码看着太舒服了,容易理解
posted @ 2020-10-12 23:55  无聊的家伙  阅读(145)  评论(0编辑  收藏  举报