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

源码仓库地址:https://git.coding.net/deervw/four-operations.git

一、需求分析

    1.程序可从命令行接收一个输入参数n,然后随机产生n道加减乘除习题。

    2.每个数字在 0 和 100 之间,运算符3到5个。

    3.每道习题要包含3-5种运算符。

    4.所出的练习题在运算过程中不得出现负数与非整数。

    5.将学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中。

    6.支持有括号的运算式,包括出题与求解正确答案;算式中存在的括号必须大于2个,且不得超过运算符的个数。

二、功能设计

    1.基本功能:能够根据用户输入的参数n随机产生n道符合要求的练习题,自动算出答案,并将式子与答案以文档的形式呈现。

    2.扩展功能:支持有括号的运算式,包括出题与求解正确答案。(注意,算式中存在的括号必须大于2个,且不得超过运算符的个数。)

三、设计实现

    我设计了一个主函数和两个自定义函数,都在Main.java

    1.判断类

    功能:带参的有返回值的类,返回的是一个整数。通过返回的参数大小,从而来判断符号的优先级。

    关系:可以直接调用。

    2.共因数divisor类

    功能:带参的有返回值的类,返回的是一个整数,该整数是两个数的最大公因数。此类通过计算两个数的最大公因数,生成化简的最终结果。

    关系:可以直接调用。

四、算法详解

    1.输入产生的题目数n:判断输入的数是否为整数,若不是则抛出异常。

try {
    n=Integer.parseInt(args[0]);
    if(n<=0)System.out.println("请重新输入整数!");
}
catch (Exception e){
    System.out.println("请重新输入整数!");
}

 

    2.生成随机数和运算符:将运算符存在一个数组arr里,以便可以随机生成;用rand()方法可以随机生成0-1之间的数;若不能整除或出现负数可以使用循环重新生成a和b,直到满足要求为止。

char[] arr= {'+','-','*','÷'};
int temp=rand.nextInt(4);
charArr=arr[temp];
int a=rand.nextInt(100);//数字范围 
int b=rand.nextInt(100);
while(a%b!=0)//判断整除
{
  a=rand.nextInt(100);
  b=rand.nextInt(99)+1;//不能出现0
}
while(a<b)//判断是否出现负数
{
  a=rand.nextInt(100);
  b=rand.nextInt(100);
}

       3.最大公因数:保证生成的为真分数,用辗转相除法

public static int divisor(int x,int y){
    while(1){
        if(x%y==0)return y;
        int temp=y;
        y=x%y;
        x=temp;
    }
}

      4.计算后缀表达式:我创建了一个栈st,保存的是char类型的字符,然后我int了一个op1来分别保存两个栈顶的操作数,所以这里先用到了String.valueOf方法先将char转换成String,然后使用Integer.parseInt方法。

 int op1 = Integer.parseInt(String.valueOf(st.pop()));
 int op2 = Integer.parseInt(String.valueOf(st.pop()));
 int op = op1 - op2;
 lkj.push(op);

五、测试运行 

六、代码展示

    将中序表达式转化为后序表达式:

static bool isNumber(string message)
        {
            //判断是否为整数字符串    
            int result = -1;   
            try
            {
                result = Convert.ToInt32(message);
                return true;
            }
            catch
            {
                return false;
            }
        }
        //判断操作符优先级大小  
        static bool comparePriority(string op1, string op2)
        {
            return getPriorityValue(op1) > getPriorityValue(op2);
        }

        private static int getPriorityValue(string op1)
        {
            throw new NotImplementedException();
        }

        static Stack<string> changeExpression(List<string> beforeExps)
        {
            Stack<string> operand = new Stack<string>();//操作数  
            Stack<string> opertor = new Stack<string>();//操作符  
            //遍历中序表示  
            int length = beforeExps.Count;
            int len = opertor.Count;
            //判断是否为操作数    
            for (int i = 0; i < length; i++)
            {
                string c = beforeExps[i];
                if (isNumber(c))
                {
                    //操作数 存在操作数栈  
                    operand.Push(c);
                }
                else
                {
                    if (c == "(")
                    {
                        opertor.Push(c);
                    }
                    else if (c == ")")
                    {
                        //该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,将"("出栈    
                        while (opertor.Peek() != "(")
                        {
                            string stringvalue = opertor.Pop();
                            operand.Push(stringvalue);
                        }
                        opertor.Pop();
                    }
                    else
                    { 
                        if (len <=  0)
                        {
                            opertor.Push(c);
                            continue;
                        }//符合为左括号 直接存入运算符  
                        if (opertor.Peek() == "(")
                        {
                            opertor.Push(c);
                        }
                        else
                        {
                            //若比运算符堆栈栈顶的运算符优先级高或相等,则直接存入运算符堆栈。    
                            if (comparePriority(c, opertor.Peek()))
                            {
                                opertor.Push(c);
                            }
                            else
                            {
                                // 若比运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。  
                                string stringvalue = opertor.Pop();
                                operand.Push(stringvalue);
                                opertor.Push(c);
                            }
                        }
                    }
                }
            }
            //依序取出运算符到操作数堆栈 
            while (len > 0)
            {
                string stringvalue = opertor.Pop();
                operand.Push(stringvalue);
            }
            //获取正常的后缀表达式  
            Stack<string> resultSt = new Stack<string>();
            while (len > 0)
            {
                string stringvalue = operand.Pop();
                resultSt.Push(stringvalue);
            }
            return resultSt;
        }

七、总结:

      所谓“模块化”就是要功能独立,功能块里要明确和调试好函数之间的调用关系,以便降低程序的复杂度和更进一步的维护。比如,在我的程序中,产生随机数和计算题目这两个功能是分开的,只要在产生随机数的程序里确定好操作数是合法的,那么计算部分可以和操作数之间做到完全独立。

八、展示PSP

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

     

  一开始我认为该程序的难点在于代码仓库的使用,这一部分没有学过,所以我以为在这上面会花掉很长的时间。但是后来学习了一段时间才发现这其实并不是难点,更难的在于代码部分,我平时没有在做代码练习,所以我的代码能力非常薄弱,感觉什么都不会,一切都得从头开始。我投入了大量的时间和精力,才勉强完成了任务,可是我还是有很多不满意的地方,但只能以后慢慢学习了。

 
 

 

posted @ 2018-03-24 23:45  付淅_nenu  阅读(175)  评论(2编辑  收藏  举报