小学四则运算练习软件项目报告

时间:2023-02-14 13:02:20

Github项目地址:

      https://github.com/Liudanxixi/calculate/tree/master

1、需求分析

  • 程序可接收一个输入参数n,然后随机产生n道加减乘除练习题.
  • 每个数字在 0 和 100 之间,运算符在3个到5个之间。
  • 每个练习题至少要包含2种运算符。同时
  • 你所出的练习题在运算过程中不得出现负数与非整数,比如不能出 3/5+2=2.6,2-5+10=7等算式。
  • 练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt中。
  • 不要输出额外信息,文件目录与程序目录一致。
  • 分母不能为0。
2、功能设计
    • 基本功能:程序可接收一个输入参数n,然后随机产生n道加减乘除练习题.
    • 基本功能:每个数字在 0 和 100 之间,运算符在3个到5个之间。
    • 基本功能:每个练习题至少要包含2种运算符。同时
    • 基本功能:你所出的练习题在运算过程中不得出现负数与非整数,比如不能出 3/5+2=2.6,2-5+10=7等算式。
    • 基本功能:练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt中。
    • 基本功能:不要输出额外信息,文件目录与程序目录一致。
    • 基本功能:分母不能为0。
    • 扩展功能:支持有括号的运算式,包括出题与求解正确答案。注意,算式中存在的括号必须大于2个,且不得超过运算符的个数。
    • 扩展程序:功能支持真分数的出题与运算,例如:1/6 + 1/8 + 2/323/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6。
3、设计实现
   下面为我的设计思路图 (注明后面加类表示类,后面加方法表示写成了方法,后面加过程表示没用调用,直接写到方法里面程序的作用):
    小学四则运算练习软件项目报告

4、测试运行

        控制台的结果如下:

          小学四则运算练习软件项目报告

       result的结果如下(其中分数只做了加法,括号也只在三为运算数中出现):

           小学四则运算练习软件项目报告

 

 

5、核心代码

 (1)用java中的异常处理控制用户输入正确

 while(n<1||n>1000)
  {
          try{
             n=in.nextInt();
          }catch(Exception e){
              System.out.println("您的输入有误,请重新输入");
              in.next();
              n=0;
        }
         if((n<1||n>1000)&&n!=0)
             System.out.println("您的输入有误,请重新输入");
  }                

(2)下面的方法为分式化解

//下面的方法为分式化解

    public static String simplification(int a,int b){
        int y = 1;
//求分子分母的最小公因数
        for(int i=a;i>=1;i--){
            if(a%i==0&&b%i==0){
                y = i;
                break;
            }
        }
        int z = a/y;
        int m = b/y;
        if(z==0) {//分子为0怎计算结果为0
            return "0";
        }
        return ""+z+"/"+m;
    }   

(3)下面的方法为利用逆波兰的方法排除生成式子为负数的情况并算出表达式的结果(这些代码是我借鉴网上的,但是我都看懂了,添加了注释,并加了自己需要的内容,)

程序大概的算法过程为边存边运算,当符号遇到的是=、)时直接计算,遇到+、- 、*、/时,看符号栈内有无其他运算符(“(”除外),如果有利用Map存放的优先级比较之后再考虑计算还是压入栈呢。

public static int jisuan(String s) {
                Stack<Integer> stack1 = new Stack<Integer>();//放数字
                Stack<String> stack2 = new Stack<String>();//放操作符
                HashMap<String, Integer> hashmap = new HashMap<String, Integer>();//存放运算符优先级的
                hashmap.put("(", 0);
                hashmap.put("+", 1);
                hashmap.put("-", 1);
                hashmap.put("*", 2);
                hashmap.put("/", 2);
                //int tiao = 0;
                for (int i = 0; i < s.length();) {
                    StringBuffer digit = new StringBuffer();  //StringBuffer类中的方法主要偏重于对于字符串的变化,
                                 //      例如追加、插入和删除等,这个也是StringBuffer和String类的主要区别。
                    char c = s.charAt(i);//将式子字符串切割为c字符
                    while (Character.isDigit(c)) {//判断字符是否为10进制数字,将一个数加入digit
                        digit.append(c);
                        i++;
                        c = s.charAt(i);
                    }
                    if (digit.length() == 0) 
                    {  //当前digit里面已经无数字,即当前处理符号
                        switch (c) {
                            case '(': {
                                stack2.push(String.valueOf(c));//如果是(   转化为字符串压入字符栈
                                break;
                                }
                            case ')': {//遇到右括号了计算,因为(的优先级最高
                                String stmp = stack2.pop();//如果是),将符号栈栈顶元素取到
                                while (!stack2.isEmpty() && !stmp.equals("(")) { //当前符号栈里面还有+ - * /
                                    int a = stack1.pop();  //取操做数a,b
                                    int b = stack1.pop();
                                    int sresulat = calculate(b, a, stmp);//计算
                                    if(sresulat<0)
                                        return  -1;
                                    stack1.push(sresulat);//将结果压入栈
                                    stmp = stack2.pop();//符号指向下一个计算符号
                                }
                                break;
                                }
                            case '=': {//遇到等号了计算,
                                String stmp;
                                while (!stack2.isEmpty()) {//当前符号栈里面还有+ - * /,即还没有算完
                                    stmp = stack2.pop();
                                    int a = stack1.pop();
                                    int b = stack1.pop();
                                    int sresulat = calculate(b, a, stmp);
                                    if(sresulat<0)
                                        return  -1;
                                    stack1.push(sresulat);
                                    }
                                    break;
                                    }
                            default: {  //不满足之前的任何情况
                                String stmp;
                                while (!stack2.isEmpty()) { //如果符号栈有符号
                                    stmp = stack2.pop(); //当前符号栈,栈顶元素
                                    if (hashmap.get(stmp) >= hashmap.get(String.valueOf(c))) {//比较优先级
                                        int a = stack1.pop();
                                        int b = stack1.pop();
                                        int sresulat =calculate (b, a, stmp);
                                        if(sresulat<0)
                                            return  -1;
                                        stack1.push(sresulat);
                                        } 
                                    else {
                                        stack2.push(stmp);
                                        break;
                                        }

                                }
                                stack2.push(String.valueOf(c));  //将符号压入符号栈
                                break;
                                }
                            }
                        } 
                    else {//处理数字,直接压栈
                        stack1.push(Integer.valueOf(digit.toString()));
                        continue;
                        }
                        i++;
                    }
                return stack1.peek();  //返回栈底数字即等式的答案。
            }
            public static int calculate(int a, int b, String stmp) {//计算a stmp b的值
                int res = 0;//存结果
                char s = stmp.charAt(0);
                switch (s) {
                  case '+': {
                    res = a + b;
                    break;
                  }
                  case '-': {
                    res = a - b;

                    break;
                  }
                  case '*': {
                    res = a * b;
                    break;
                  }
                  case '/': {
                      if(b==0)
                          return -1;
                      else
                          res = a / b;
                    break;
                  }
                }
                return res;
            }

6、总结:模块划分我在设计实现那一模块中的那张图已经展示的很清楚了。先生成运算式(提前排除分母为0和除成小数的问题)

7、展示PSP

PSP2.1

任务内容

计划完成需要的时间(min)

实际完成需要的时间(min)

Planning

计划

10

6

·       Estimate

·  估计这个任务需要多少时间,并规划大致工作步骤

10

6

Development

开发

480

530

··       Analysis

  需求分析 (包括学习新技术)

10

10

·       Design Spec

·  生成设计文档

5

6

·       Design Review

·  设计复审 (和同事审核设计文档)

5

4

·       Coding Standard

  代码规范 (为目前的开发制定合适的规范)

10

10

·       Design

  具体设计

30

20

·       Coding

  具体编码

240

300

·       Code Review

·  代码复审

120

100

·       Test

·  测试(自我测试,修改代码,提交修改)

60

80

Reporting

报告

15

23

··       Test Report

·  测试报告

3

2

·       Size Measurement

  计算工作量

2

1

·       Postmortem & Process Improvement Plan

·  事后总结 ,并提出过程改进计划

10

20

  根据上表我的代码编写和修改占用的大量时间,原因是没有提前设计好,以至于用的算法不好,导致编写代码不够精炼,问题也多。

  小结:设计重要,不能直接写代码,需要设计好了再写。

  我把这个程序写复杂了,出现了大量的代码,完全可以将出现负数、分母为0,除下来是小数等问题直接放到逆波兰方法中排除,也就是不管式子生成的合不合适,先生成,在逆波兰的计算中不合适了在重新生成一个式子就可以了,这样写代码精炼,通用性比较高,想处理更多运算数的情况很好添加,但是计算机的运行效率低了,因为不提前判断式子是否符合规定,这样产生不合适的式子会很多,每次都需要重新生成。

  而我的做法是先排除了分母为0、和除下来是小数的问题,这样大大增加了写代码的复杂度,代码不够精炼,而且不通用,如果题目变成了7个运算符我还得重新加大量代码,但是会提前排除一部分无效工作。

  两种方法相比较而言第一种方法更好,但是我是在写代码的过程中发现那些写更好的,但是已经写了大半了,所以就没有改。

  如果提前设计好,想清楚这些问题就不会写完了才发现更好的方法,所以写代码之间要多考虑,设计清楚了再写。