zoukankan      html  css  js  c++  java
  • 20162311 结对编程项目-四则运算 整体总结

    20162311 结对编程项目-四则运算 整体总结

    一、需求分析

    • 支持多运算符
    • 支持真分数
    • 用户可选择生成题目的等级
    • 处理生成题目并输出到文件
    • 完成题目后从文件读入并判题

    后续拓展的可能

    • 多语言支持

    备注:本周的需求分析为上周的拓展需求

    二、设计思路

    在上周的基础上进行改进,首先增加了真分数。修改Operad类中的getOp1getOp2方法,使其随机生成整数或真分数。接下来是支持多运算符,新建了一个MakeQuestions类,具体代码在第三点进行解释。然后将原来的含有main函数的类分为两个类,一个是OutInputQuestionsToFile,另一个是OutputResultToFile。即一个用来生成题目并输出到文件,另一个用来文件读入并判题,再将结果输出到文件。

    UML类图

    三、实现过程中的关键代码

    • 支持真分数
    package Arithmetic;
    
    /**
     * Created by Administrator on 2017/5/15.
     */
    //********************************************************************
    //  RationalNumber.java       Java Foundations
    //
    //  Represents one rational number with a numerator and denominator.
    //********************************************************************
    
    public class RationalNumber
    {
        private int numerator, denominator;
    
        //-----------------------------------------------------------------
        //  Constructor: Sets up the rational number by ensuring a nonzero
        //  denominator and making only the numerator signed.
        //-----------------------------------------------------------------
        public RationalNumber (int numer, int denom)
        {
            if (denom == 0)
                denom = 1;
    
            // Make the numerator "store" the sign
            if (denom < 0)
            {
                numer = numer * -1;
                denom = denom * -1;
            }
    
            numerator = numer;
            denominator = denom;
    
            reduce();
        }
    
        //-----------------------------------------------------------------
        //  Returns the numerator of this rational number.
        //-----------------------------------------------------------------
        public int getNumerator ()
        {
            return numerator;
        }
    
        //-----------------------------------------------------------------
        //  Returns the denominator of this rational number.
        //-----------------------------------------------------------------
        public int getDenominator ()
        {
            return denominator;
        }
    
        //-----------------------------------------------------------------
        //  Returns the reciprocal of this rational number.
        //-----------------------------------------------------------------
        public RationalNumber reciprocal ()
        {
            return new RationalNumber (denominator, numerator);
        }
    
        //-----------------------------------------------------------------
        //  Adds this rational number to the one passed as a parameter.
        //  A common denominator is found by multiplying the individual
        //  denominators.
        //-----------------------------------------------------------------
        public RationalNumber add (RationalNumber op2)
        {
            int commonDenominator = denominator * op2.getDenominator();
            int numerator1 = numerator * op2.getDenominator();
            int numerator2 = op2.getNumerator() * denominator;
            int sum = numerator1 + numerator2;
    
            return new RationalNumber (sum, commonDenominator);
        }
    
        //-----------------------------------------------------------------
        //  Subtracts the rational number passed as a parameter from this
        //  rational number.
        //-----------------------------------------------------------------
        public RationalNumber subtract (RationalNumber op2)
        {
            int commonDenominator = denominator * op2.getDenominator();
            int numerator1 = numerator * op2.getDenominator();
            int numerator2 = op2.getNumerator() * denominator;
            int difference = numerator1 - numerator2;
    
            return new RationalNumber (difference, commonDenominator);
        }
    
        //-----------------------------------------------------------------
        //  Multiplies this rational number by the one passed as a
        //  parameter.
        //-----------------------------------------------------------------
        public RationalNumber multiply (RationalNumber op2)
        {
            int numer = numerator * op2.getNumerator();
            int denom = denominator * op2.getDenominator();
    
            return new RationalNumber (numer, denom);
        }
    
        //-----------------------------------------------------------------
        //  Divides this rational number by the one passed as a parameter
        //  by multiplying by the reciprocal of the second rational.
        //-----------------------------------------------------------------
        public RationalNumber divide (RationalNumber op2)
        {
            return multiply (op2.reciprocal());
        }
    
        //-----------------------------------------------------------------
        //  Determines if this rational number is equal to the one passed
        //  as a parameter.  Assumes they are both reduced.
        //-----------------------------------------------------------------
        public boolean isLike (RationalNumber op2)
        {
            return ( numerator == op2.getNumerator() &&
                    denominator == op2.getDenominator() );
        }
    
        //-----------------------------------------------------------------
        //  Returns this rational number as a string.
        //-----------------------------------------------------------------
        public String toString ()
        {
            String result;
    
            if (numerator == 0)
                result = "0";
            else
            if (denominator == 1)
                result = numerator + "";
            else
                result = numerator + "/" + denominator;
    
            return result;
        }
    
        //-----------------------------------------------------------------
        //  Reduces this rational number by dividing both the numerator
        //  and the denominator by their greatest common divisor.
        //-----------------------------------------------------------------
        private void reduce ()
        {
            if (numerator != 0)
            {
                int common = gcd (Math.abs(numerator), denominator);
    
                numerator = numerator / common;
                denominator = denominator / common;
            }
        }
    
        //-----------------------------------------------------------------
        //  Computes and returns the greatest common divisor of the two
        //  positive parameters. Uses Euclid's algorithm.
        //-----------------------------------------------------------------
        private int gcd (int num1, int num2)
        {
            while (num1 != num2)
                if (num1 > num2)
                    num1 = num1 - num2;
                else
                    num2 = num2 - num1;
    
            return num1;
        }
    }
    
    

    这是一个有理数的类,每一个对象都代表一个有理数。构造方法含有分子和分母两个参数,我只需限定分子小于分母,那么就可以得到一个真分数。在Operad类中,有两个私有方法

     private String  getA() {
            a = String.valueOf(rnd1.nextInt(10) + 1);
            return a;
        }
    
     private RationalNumber getB(){
            while (true) {
                c = rnd1.nextInt(10) + 1;
                d = rnd2.nextInt(10) + 1;
                b = new RationalNumber(c, d);
                if (c < d){
                    break;
                }
            }
    

    A是一个随机整数,我把它的范围设为1~10,B是一个随机真分数。而每一个操作数既可能为整数,也可能为真分数

    public String getOp1(){
            if (rnd3.nextInt(2) == 0){
                op1 = getA();
            }
            else
                op1 = getB().toString();
            return op1;
        }
    
     public String getOp2(){
            if (rnd3.nextInt(2) == 0){
                op2 = getA();
            }
            else
                op2 = getB().toString();
            return op2;
        }
    

    这样就实现了真分数。

    • 支持多运算符
     public String getExper(int i){
            expr = opd.getOp1() + getOperator();
            for (int j = 0; j < i-1; j++) {
                String s = opd.getOp1() + getOperator();
                expr += s;
            }
            expr = expr + opd.getOp2();
            return expr;
        }
    

    我用循环的方法来获得多运算符。参数i代表运算符的个数。getOperator方法也是私有的,可以随机生成一个运算符(加、减、乘、除)。

    • 处理生成题目并输出到文件
    package Arithmetic;
    
    import java.io.*;
    
    /**
     * Created by Administrator on 2017/5/19.
     */
    public class IOFile {
        PrintStream  ps;
    
    
        public IOFile(String file){
            try {
                ps = new PrintStream(file);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        public void WriteQuestionsToFile(String s){
            ps.append(s);// 在已有的基础上添加字符串
        }
    }
    

    这个类只有一个方法,用来将题目写入文件。用到了PrinterStream类和它的append方法。在InputQuestionsToFile类中,只需调用该方法,并将表达式作为一个字符串传入参数即可。

    • 完成题目后从文件读入并判题
     public static void main(String[] args) throws IOException{
            Judgement jdg = new Judgement();
            NifixToSuffix nts = new NifixToSuffix();
            NumberFormat fmt = NumberFormat.getPercentInstance();
            FileInputStream fis = new FileInputStream("Exercises.txt");
            InputStreamReader isr = new InputStreamReader(fis);
            BufferedReader in = new BufferedReader(isr);
            StringTokenizer tokenizer1 = null, tokenizer2 = null;
            String token1, token2, token3, token4;
            String s1 = null;
            String str;
            int q = 0, count = 0;
            IOFile iof = new IOFile("ExercisesResult.txt");
            while ((str = in.readLine()) != null) {
                tokenizer1 = new StringTokenizer(str, ":");
                token1 = tokenizer1.nextToken();
                token2 = tokenizer1.nextToken();
                tokenizer2 = new StringTokenizer(token2, "=");
                token3 = tokenizer2.nextToken();
                token4 = tokenizer2.nextToken();
                nts.conversion(token3);
                if (token4.equals(jdg.evaluate(nts.getMessage()))) {
                    s1 = "正确!";
                    q++;
                } else {
                    s1 = "错误,正确答案为:" + jdg.evaluate(nts.getMessage());
                }
    
                String s2 = str + "
    " + s1 + "
    
    ";
                iof.WriteQuestionsToFile(s2);
                count++;
            }
    
                double accuracy = (double) q / count;
                String s3 = "完成" + count + "道题目,答对" + q + "道题,正确率为" + fmt.format(accuracy);
                iof.WriteQuestionsToFile(s3);
    
        }
    

    以上是主要代码。因为文件中每个题目都是一行,且形式都为“题目1:表达式 =答案”。所以我用了BufferedReader中的readLine方法,可以一行一行的读取。读取一行之后,先以“:”为标记将字符串分开。把“题目n”赋值给token1,“表达式 =答案”赋值给token2,再将token2以“=”为标记,把表达式赋值给token3,答案赋值给token4。将token3转化为后缀表达式,进行计算,把计算结果与token4进行比较,再把整个结果写入到文件中。这样一直循环,直到文件中没有下一行为止。

    • 实现分数的运算

    还是用RationalNumber类。其中定义好了加减乘除四种方法。在栈中进行计算时,每取出一个操作数,我都先将其转化为RationalNumber类型再计算。RationalNumber类中有一个toString方法,返回这个有理数的值,是String类型。具体转化方法如下

    public RationalNumber tranIntoRationalNum (String s){
            String token1, token2;
    
            StringTokenizer tokenizer1 = new StringTokenizer(s, "/");
            token1 = tokenizer1.nextToken();
            if (tokenizer1.hasMoreTokens()) {
                token2 = tokenizer1.nextToken();
                r = new RationalNumber(Integer.parseInt(token1), Integer.parseInt(token2));
            }
            else {
                r = new RationalNumber(Integer.parseInt(token1),1);
            }
            return r;
        }
    

    把每个操作数(String类型)以“/”为标记分开,第一个字符传入分子,第二个传入分母;如只有一个,则第一个传入分子,把“1”传入分母。从栈中取出操作数时,调用这个方法,就能将操作数转化为RationalNumber类型。再调用其中的加减乘除的方法进行计算。

    private String evalSingleOp (char operation, RationalNumber op1, RationalNumber op2)
        {
            RationalNumber result = new RationalNumber(0,1);
    
            switch (operation)
            {
                case ADD:
                    result = r1.add(r2);
                    break;
                case SUBTRACT:
                    result = r1.subtract(r2);
                    break;
                case MULTIPLY:
                    result = r1.multiply(r2);
                    break;
                case DIVIDE:
                    result = r1.divide(r2);
            }
    
            return result.toString();
        }
    

    最终返回结果,为String类型。

    四、测试方法

    • CalculatorTest

    • NifixToSuffixTest

    • RationalNumberTest

    五、运行过程截图

    • Step One:运行OutputQuestionsToFile类,将生成的题目输出到Exercises.txt文件中

    • Step Two:运行OutputResultToFile类,将结果输出到ExercisesResult.txt文件中

    六、代码托管地址

    七、遇到的困难及解决方法

    • 问题1

    在实现了生成含真分数的表达式后,计算出了问题。因为是转化为后缀表达式,通过栈的方法计算,之前的都是整数,只需将String类型用Integer.praseInt()方法转化成Int类型就可以了。可是加了真分数后,例如1/3,是一个字符串,在转化时就出错了。

    • 解决办法

    原来的Judgement类中定义的栈中的元素是Int类型,我把它改成String类型,这样真分数就能进栈了,至于其它的Int类型的整型数,我修改了一下Operad类,将getOp1()getOp2()这两个方法的返回值都设置成String类型,如果生成的随机数为整型数,就用String.valueOf()方法将其转化为String类型。具体的代码在第三点实现过程中的关键代码有解释

    • 问题2

    解决问题1后,随之而来的是计算问题。虽然将真分数压进了栈,但是之前定义的计算都是整型数的计算,真分数和真分数,真分数和整型数之间的计算并没有定义。

    • 解决办法

    使用教材中的RationalNumber类。这个类的每一个对象都是一个有理数,里面定义了有理数的四则运算。我只需把每个操作数转化为RationalNumber类型,在调用其中的方法计算就好了,具体的解释参见第三点实现过程中的关键代码

    八、对结对的小伙伴的评价

    这周我设计代码,同时教我的搭档将我的设计实现,不过搭档写的是一些比较简单的代码。从上周什么也不会写,到现在能够根据我的设计思路写出一点东西,还是有进步的,最后加注释和修改类名也交给她做了。虽然有些进步,但还是有很多需要改进的地方。首先,最好再去巩固一下课本的知识,在讲解的过程中,发现有些书上的基本知识都不清楚,这样我讲起来有些费劲;其次,要有自己的思路,要学会自己设计程序并实现;最后,要多利用课余时间补上之前没学清楚的东西。希望在以后结对编程中能赶上步伐。

    九、参考或引用的设计、实现

    • RationalNumber

    这个类是教材上定义好的一个类,使用它生成真分数,并进行计算,得到的结果也可以用分数形式展现。

    • 处理生成题目并输出到文件

    参考java的io操作(将字符串写入到txt文件中)

    • 完成题目后从文件读入并判题

    参考从文本文件中读取数据(每一行为一个字符串数据)

    十、PSP

    PSP2.1 Personal Software Process Stages 预估耗时(小时) 实际耗时(小时)
    Planning 计划
    · Estimate · 估计这个任务需要多少时间 0.5 0.5
    Development 开发
    · Analysis · 需求分析 (包括学习新技术) 2 4
    · Design Spec · 生成设计文档 1.5 1
    · Design Review · 设计复审 (和同事审核设计文档) 1 1
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 0.5 1
    · Design · 具体设计 2 3
    · Coding · 具体编码 6 8
    · Code Review · 代码复审 2 2.5
    · Test · 测试(自我测试,修改代码,提交修改) 2 2.5
    Reporting 报告
    · Test Report · 测试报告 1 1.5
    · Size Measurement · 计算工作量 0.5 0.5
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 0.5 0.5
    合计 19.5 26
  • 相关阅读:
    Apache 虚拟主机 VirtualHost 配置
    EAX、ECX、EDX、EBX寄存器的作用
    Python中文文档 目录(转载)
    八度
    POJ 3268 Silver Cow Party (最短路)
    POJ 2253 Frogger (求每条路径中最大值的最小值,Dijkstra变形)
    2013金山西山居创意游戏程序挑战赛——复赛(1) HDU 4557 非诚勿扰 HDU 4558 剑侠情缘 HDU 4559 涂色游戏 HDU 4560 我是歌手
    HDU 4549 M斐波那契数列(矩阵快速幂+欧拉定理)
    UVA 11624 Fire! (简单图论基础)
    HDU 3534 Tree (树形DP)
  • 原文地址:https://www.cnblogs.com/-zzr-/p/6882425.html
Copyright © 2011-2022 走看看