结队编程--java实现
1、GitHub地址:https://github.com/caiyouling/Myapp
队友:钟小敏 GitHub地址:https://github.com/zhongxiao136/Myapp
2.PSP表格
| PSP | Personal Software Process Stages | 预计耗时(分钟) | 实际耗时(分钟) | 
|---|---|---|---|
| Planning | 计划 | 40 | 30 | 
| .Estimate | .估计这个任务需要多少时间 | 40 | 30 | 
| Development | 开发 | 1320 | 1330 | 
| .Analysis | .需求分析 | 100 | 120 | 
| .Design Spec | .生成设计文档 | 40 | 30 | 
| .Design Review | .设计复审 | 15 | 20 | 
| .Coding Standard | .代码规范 | 15 | 20 | 
| .Design | .具体设计 | 90 | 100 | 
| .Coding | .具体编码 | 850 | 950 | 
| .Code Review | .代码复审 | 90 | 90 | 
| .Test | .测试(自我测试,修改代码,提交修改) | 120 | 130 | 
| Reporting | 报告 | 180 | 160 | 
| .Test Report | .测试报告 | 90 | 80 | 
| .Size Measurement | .计算工作量 | 30 | 40 | 
| .Postmortem&Process Improvement Plan | .事后总结,并提出过程改进计划 | 60 | 40 | 
| 合计 | 1540 | 1520 | 
效能分析


设计实现过程
- 解题思路:由于题目为自动生成小学四则运算题目,所以需要系统随机生成运算式。运算式包含操作数和运算符,所以我们就用了一个CreateNumAndOpe类随机生成,然后利用createExpression类组合成运算式和答案(利用Calculate类进行计算,因为结果要为真分数或整数,所以我们利用Fraction类对分数进行操作化为最终形式),然后用createFile类生成题目,答案和做题文件,最后,利用Grade类进行验证答案,并生成成绩文件。
- createNumAndOpe类:定义运算符数组;包含createNumber()和createOperator(),其中createNumber()用于生成操作数(利用Random类随机生成整数或分数);createOperator()用于生成运算符,随机从运算符数组中选出运算符。
- createExpression类:用于生成随机带括号的运算式,其中exerciseAndAnswer()根据操作数数组和运算符数组生成运算式,其中需要用Random类判断是否随机生成括号,最后用Calculate()计算出结果。getExerciseAndAnswer()根据用户输入的题目个数和数字范围生成相应的题目和答案。
- Calculate类:根据运算式转为字符数组,然后逐个遍历,建立一个运算符栈和操作数栈,利用中缀表达式转为后缀表达式进行计算。
- 因为括号的优先级最高,所以我们先判断是否为'(',若是则入运算符栈;
- 判断是否为')',是则从操作数取出两个进行运算,将'('之后的运算符都拿出来算,结果再存入操作数栈中,然后左括号出栈;
- 判断是否为运算符,当运算符优先级小于运算符栈顶元素,则取两个操作数进行运算,直至栈顶优先级不高于该运算符,结果存入操作数栈中,然后该运算符入栈;
- 若为操作数,因为生成的操作数有分数,但是我们转为了字符数组,就需要根据'/'作为标志获取分子和分母,若为整数,我们将分母设为1,把操作数都转为分数进行传到calculate()进行运算,返回结果
- Fraction类:对分数进行操作,其中getFinalFraction()用于生成最终分数形式,gcd()利用辗转相除法生成最大公约数,getRealFraction()将假分数转为真分数。
- createFile类:利用System.getProperTy()获取当前路径生成printFile文件夹,利用BufferedWriter写入题目文件,答案文件和答题文件(用户进行答题的文件),存放到printFile文件夹下
- Grade类:用于验证用户答题是否正确,利用BufferedReader读取Writer文件和answer文件进行比较,算出对错的题数及题号写入Grade文件,也存在printFile文件夹下。
- 各类之间的调用如下:
  
- Frame类:图形化界面,用户点击‘生成文件’,即会生成练习文件,在左面板也会出现题目,中间面板进行答题,点击查看答案,则会有正确答案在右侧面板,查看成绩后会弹出成绩框。
关键代码说明
- 用于生成操作数和运算符的方法:1.利用随机生成的flag=[0,1],当flag为0时,生成整数 2.当flag为1时,生成分子和分母组成分数
public static String[] createNumber(int number, int round){
        Random random = new Random();
        String[] num = new String[number];
        for (int i = 0; i < number; i++) {
            //用于判断生成整数还是分数
            int flag = (int)(Math.random()*10) % 2;
            if(flag == 0){          //生成整数
                int n = random.nextInt(round);
                if(n == 0){
                    num[i] = 1 + "";
                }else{
                    num[i] = n +"";
                }
            }else{          //生成分数
                //随机生成分子和分母
                int numerator = random.nextInt(round);
                int denominator = random.nextInt(round);;
                while(numerator>=denominator || numerator==0 || denominator==0){    //判断是否为真分数,且不能生成带0的分数
                    numerator = random.nextInt(round);
                    denominator = random.nextInt(round);
                }
                //拼装成分数形式
                num[i] = numerator + "/" + denominator;
            }
        }
        return num;
    }
    /**
     * 随机生成运算符
     * 将四则运算符放入一个静态不可变的operatorTypes[]字符数组中
     * 随机产生index到数组中取操作符
     */
    private static final Character[] operatorTypes = {'+' , '-' , '×' , '÷'};
    public static Character[] createOperators(int number) {
        Character[] operators = new Character[number];
        for (int i = 0; i < number; i++) {
            //随机获取运算符的类型(0~3 代表4个运算符(+、-、×、÷)的类型)
            int index = (int)(Math.random()*4);
            Character operatorType = operatorTypes[index];
            operators[i] = operatorType;
        }
        return operators;
    }
     public static int priority(Character character) {
        switch(character) {
            case '×':
            case '÷':return 1;
            case '+':
            case '-':return 0;
        }
        return -1;
    }
     private static String[] exerciseAndAnswer(String[] numbers, Character[] operators){
        Random random = new Random();
        //获得操作数的数量
        int num = numbers.length;
        //随机生成带括号的算式
        int isAddBracket = (int)(Math.random()*10) %2;
        if(isAddBracket ==  1){    //当isAddBracket==1时,生成带括号的表达式
            //当标记为1时代表该操作数已经添加了左括号
            int[] leftBracket = new int[num];
            //当标记为1时代表该操作数已经添加了右括号
            int[] rightBracket = new int[num];
            //遍历操作数数组,随机添加括号
            for (int index = 0 ; index<num-1 ; index++) {
                int n = (int)(Math.random()*10)%2;
                if(n == 0 && rightBracket[index] != 1) {//判断当前操作数是否标记了左括号
                    leftBracket[index] = 1;     //标记已生成左括号
                    numbers[index] = "(" + numbers[index];  //操作数之前加上左括号
                    int k = num - 1;
                    //生成右括号的位置(左括号的位置~最后)
                    int rightBracketIndex = random.nextInt(k)%(k-index) + (index+1);
                    //如果当前操作数有左括号,则重新生成括号位置
                    while (leftBracket[rightBracketIndex] == 1){
                        rightBracketIndex = random.nextInt(k)%(k-index) + (index+1);
                    }
                    rightBracket[rightBracketIndex] = 1;        //标记已生成右括号
                    numbers[rightBracketIndex] = numbers[rightBracketIndex] +")";
                }
            }
        }
        //将运算符数组和操作数数组交替拼成一个算式字符串
        StringBuilder str = new StringBuilder(numbers[0]);
        for (int k = 0; k < operators.length; k++) {     //组成算式
            str.append(operators[k]).append(numbers[k + 1]);
        }
        //将算式转换为String
        String exercise = str.toString();
        //获取运算式结果
        String answer = Calculate.getAnswer(exercise);
        if(answer.equals("-")){      //运算过程出现负数则返回null
            return null;
        }
        return new String[]{exercise,answer};
    }
public static String getAnswer(String expression) {
        Stack<Character> operators = new Stack<Character>();  //运算符栈,用于存放运算符
        Stack<Fraction> fractions = new Stack<Fraction>();  //操作数栈,用于存放操作数
        if(expression == null){
            return "null";
        }
        char[] chars = expression.toCharArray();   //将表达式字符串转成字符数组
        //遍历获取处理
        for (int i = 0 ; i<chars.length ; i++) {
            char c = chars[i];
            if(c == '('){             //先处理有括号的情况,如果是左括号,入栈
                operators.push(c);
            }   else if(c == ')'){           //当前字符为右括号
                while(operators.peek() != '('){   //当运算符栈顶的元素不为‘(’,则继续
                    Fraction fraction1 = fractions.pop();   //拿取操作栈中的两个分数
                    Fraction  fraction2 = fractions.pop();
                    //获取计算后的值
                    Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
                            fraction2.getNumerator(), fraction2.getDenominator());
                    if(result.getNumerator()<0){            //判断是否为负数
                        return  "-";
                    }
                    //将结果压入栈中
                    fractions.push(result);
                }
                //将左括号出栈
                operators.pop();
            }else if(isOperator(c)){//是运算符
                //当运算符栈不为空,且当前运算符优先级小于栈顶运算符优先级
                while(!operators.empty() && !(priority(c)>= priority(operators.peek()))){
                    //拿取操作栈中的两个分数
                    Fraction fraction1 = fractions.pop();
                    Fraction  fraction2 = fractions.pop();
                    //获取计算后的值
                    Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
                            fraction2.getNumerator(), fraction2.getDenominator());
                    if(result.getNumerator()<0){
                        return  "-";
                    }
                    //将结果压入栈中
                    fractions.push(result);
                }
                //将运算符入栈
                operators.push(c);
            }else{          //是操作数
                if(c >= '0' && c <= '9'){
                    StringBuilder fra = new StringBuilder();
                    //对分式进行处理
                    while(i<chars.length && (chars[i]=='/' || ((chars[i]>='0') && chars[i]<='9'))){
                        fra.append(chars[i]);
                        i++;
                    }
                    i--;
                    //到此 fra里面是一个操作数
                    String val = fra.toString();
                    //标记‘/’的位置
                    int flag = val.length();
                    for(int j = 0 ; j<val.length() ; j++){
                        if(val.charAt(j)=='/'){//当获取的数值存在/则标记/的位置,便于接下来划分分子和分母生成分数对象
                            flag = j;
                        }
                    }
                    //把操作数拆成分式计算
                    //分子
                    StringBuilder numeratorBuf = new StringBuilder();
                    //分母
                    StringBuilder denominatorBuf = new StringBuilder();
                    for(int k = 0 ; k<flag; k++){
                        numeratorBuf.append(val.charAt(k));
                    }
                    //判断是否为分数
                    if(flag != val.length()){
                        for(int m = flag+1 ; m<val.length() ; m++){
                            denominatorBuf.append(val.charAt(m));
                        }
                    }else{//如果不是分数则分母计为1
                        denominatorBuf.append('1');
                    }
                    //将分子分母分别入栈
                    fractions.push(new Fraction(Integer.parseInt(numeratorBuf.toString()),
                            Integer.parseInt(denominatorBuf.toString())));
                }
            }
        }
        while(!operators.empty() && fractions.size()>1){
            Fraction fraction1 = fractions.pop();
            Fraction  fraction2 = fractions.pop();
            //获取计算后的值
            Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
                    fraction2.getNumerator(), fraction2.getDenominator());
            if(result.getNumerator()<0){
                return "-";
            }
            //将结果压入栈中
            fractions.push(result);
        }
        //计算结果
        Fraction result = fractions.pop();
        //获取最终的结果(将分数进行约分)
        return Fraction.getFinalFraction(result);
    }
    //用于验证答案是否正确,correct的题数用字符串拼接,再分割字符串
     public static void isAnswer(String writeFilePath, String answerFilePath) {
        //获取用户答题和练习答案的文件
        File writeFile = new File(writeFilePath);
        File answerFile = new File(answerFilePath);
        //找到存放Writer.txt和Answer.txt文件的文件夹
        String fileDirectory = System.getProperty("user.dir") + File.separator + "PrintFile";
        File gradeFile = new File(fileDirectory, "Grade.txt");//在当前文件夹下生成Grade.txt文件
        if(writeFile.isFile() && answerFile.isFile()) {
            try {
                BufferedReader writeReader = new BufferedReader(new InputStreamReader(new FileInputStream(writeFile)));
                BufferedReader answerReader = new BufferedReader(new InputStreamReader(new FileInputStream(answerFile)));
                //储存错题和对题
                String correct = "";
                String wrong = "";
                int correctNum = 0;
                int wrongNum = 0;
                //记录错题对题的题号
                int index = 1;
                String write = null;
                String answer = null;
                System.out.println("开始验证答案···");
                //通过一行行读取文件比较答题情况
                while((( write= writeReader.readLine()) != null) && ((answer = answerReader.readLine()) != null)){
                    if(write.equals(answer)){
                        //将答对的题用字符串拼接
                        correct += "," + index;
                        index ++;
                        correctNum ++;
                    }else{
                        wrong += "," + index;
                        index ++;
                        wrongNum ++;
                    }
                }
                //用于生成Grade.txt文件内容
                if(correctNum > 0){
                    correct = "Correct: " + correctNum + "(" + correct.substring(1) +")" + "\r\n" ;
                }else{
                    correct = "Correct: 0" +"\r\n";
                }
                if(wrongNum > 0){
                    wrong = "Wrong: " + wrongNum +  "(" + wrong.substring(1) +")";
                }else{
                    wrong = "Wrong: 0";
                }
                //写入文件
                BufferedWriter gradeFileBuf = new BufferedWriter(new FileWriter(gradeFile));
                gradeFileBuf.write(correct);//将correct 和wrong写入文件
                gradeFileBuf.write(wrong);
                System.out.print(correct);//控制台也打印答题情况
                System.out.println(wrong);
                if(writeReader != null){
                    writeReader.close();
                }
                if(answerReader != null){
                    answerReader.close();
                }
                if(gradeFileBuf != null){
                    gradeFileBuf.close();
                }
                 public static String getFinalFraction(Fraction fraction) {
        int denominator = fraction.getDenominator();        //获得分子分母
        int numerator = fraction.getNumerator();
        if(numerator == 0){ //若分子为0,则输出0
            return "0";
        }
        if(denominator == 0){
            return "";
        }
        int a = gcd(numerator, denominator);        //c是分子分母的最大公因数,将分子分母化简
        if (denominator == 1 ) {   //分母为1
            return numerator + "";
        }
        if(numerator == denominator){
            return 1+"";
        }else {
            if(numerator > denominator){           //分子大于分母化为真分数
                fraction = getRealFraction(fraction);	//假分数分割为商和余数,余数作为新的分子
                if(fraction.getNumerator() == 0){//若余数为0,则代表整除
                    return fraction.getInter()+"";
                }else {
                    return fraction.getInter() + "'" + fraction.getNumerator()/a + "/" + fraction.getDenominator()/a;
                }
            }else{              //其他情况化简分数
                return numerator/a + "/" + denominator/a;
            }
        }
    }
    private static int gcd(int numerator, int denominator){
        if(numerator%denominator == 0){
            return denominator;         //获取最大公因数
        }else{
            return gcd(denominator,numerator%denominator);    //辗转相除法递归获取最大公因数
        }
    }
测试
1.启动main,运行控制台打印

2.用户输入后生成的文件列表
.
3.生成题目文件的题目

4.生成答案放入文件

5.在答题文件进行答题后

6.重新判别答案

7.传入参数不正确的情况

8.输入不正确的文件名

9.生成一万道题目

10.生成一万道题目的答案文件

11.初始界面

12.点击生成文件

13.输入生成个数

14.生成题目

15.进行作答

16.输出成绩

17.生成十道题

18.生成一万道

小结
- 本次结对项目中,我们都受益匪浅,结对项目需要多次讨论得出一致结论,两个人一起分析问题解决bug是比一个人好一些,虽然也有发生歧义的地方,但是不同角度看问题,才能实现1+1>2,这次编程作业让我们对java有了更深了了解,但也还有很多不足的
结队编程--java实现的更多相关文章
- 20175126Apollo  20175126《Java程序设计》结队编程项目——四则运算
		结队编程项目——四则运算 一.项目需求 自动生成小学四则运算题目(加.减.乘.除)统计正确率 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 需求分析: 生成四则运算:需要使用 ... 
- 结队编程study
		##今天针对study进行结队编程,我和搭档张佳慧刚开始误认为SystemBarTintManager,baseActivity是personalActivity跳转的下一个界面,因为这个代码个人中心 ... 
- 结队编程第二次作业:Android自动生成算式应用
		一.题目要求 本次作业要求两个人合作完成,驾驶员和导航员角色自定,鼓励大家在工作期间角色随时互换,这里会布置两个题目,请各组成员根据自己的爱好任选一题. 这次我和我的小伙伴选择了题目一. 题目1: 实 ... 
- Atitit.异步编程 java .net php python js 对照
		Atitit.异步编程 java .net php python js 的比較 1. 1.异步任务,异步模式, APM模式,, EAP模式, TAP 1 1.1. APM模式: Beg ... 
- Atitit.异步编程 java .net php python js 的比较
		Atitit.异步编程 java .net php python js 的比较 1. 1.异步任务,异步模式, APM模式,, EAP模式, TAP 1 1.1. APM模式: Beg ... 
- Java并发编程-Java内存模型
		JVM内存结构与Java内存模型经常会混淆在一起,本文将对Java内存模型进行详细说明,并解释Java内存模型在线程通信方面起到的作用. 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存 ... 
- UDP协议网络Socket编程(java实现C/S通信案例)
		我的博客园:https://www.cnblogs.com/chenzhenhong/p/13825286.html 我的CSDN博客:https://blog.csdn.net/Charzous/a ... 
- socket编程-java
		一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ... 
- 基础编程-java之股神
		买了一支股票,他知道从他买股票的那天开始,股票会有以下变化:第一天不变,以后涨一天,跌一天,涨两天,跌一天,涨三天,跌一天...依此类推. 为方便计算,假设每次涨和跌皆为1,股票初始单价也为1,请计算 ... 
随机推荐
- H3C交换机如何删除VLAN
			H3C交换机如何删除VLAN,如果直接使用“undo vlan”是删不干净的,因为配置VLAN时还配置过接口. 1.首先通过console口或telnet连接三层交换机,进入用户视图模式”<H3 ... 
- HDFS修改副本数,并生效。
			1.hadoop集群使用的ucloud的uahdoop 2.是公司集群配置小,只有两台core节点,实际就是两台的datanode. 容量占用超过了80%,需要缩减副本以空出容量. 3.查看 hado ... 
- vue---将json导出Excel的方法
			在做数据表格渲染的时候,经常遇到的需求的就是将导出excel,下面是使用vue进行项目数据进行导出的方法. 一.安装依赖 npm i -S file-saver npm i -S xlsx 二.在sr ... 
- Java数组移位和统计
			package com.imooc.method; import java.util.InputMismatchException; import java.util.Scanner; public ... 
- Hive学习笔记——metadata
			Hive结构体系 https://blog.csdn.net/zhoudaxia/article/details/8855937 可以在hive的jdbc接口中使用getMetaData方法来获取hi ... 
- light4j一个轻量级的低延时、高吞吐量、内存占用量小的API平台
			1.背景(abstract) 笔者算是一个极客类型的程序员了.喜欢探索一些程序内在的原理.稳定性.自动化运维.健壮性,很多时间也会 去对程序的内存使用率.cpu使用率锱铢必较.尽量克扣掉不必要的cpu ... 
- Python - Django - 中间件 process_template_response
			process_template_response(self, request, response) 有两个参数,response 是 TemplateResponse 对象(由视图函数或者中间件产生 ... 
- 【论文阅读】3DMM-A Morphable Model for The Synthesis of 3D Faces
			前言 参考 1. 2. 完 
- 头部文件jq嵌入笔记
			var headHtml = '<div class="container">' + '<div class="navbar-header"& ... 
- nginx配置优化提高并发量
			1 nginx配置优化提高并发量 worker_processes 2; 这个按照CPU的核数来决定 2 worker_connections 65535; 这个一般设置65535即可 每个进程允许的 ... 
