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

1、实验目的与要求
(1)掌握软件项目个人开发流程。
(2)掌握Coding.net上发布软件项目的操作方法。

2、实验内容和步骤
任务1:
尝试按照《构建之法》第2章中2.3所述PSP流程,使用JAVA编程语言,独立完成一个3到5个运算符的四则运算练习的命令行软件开发。

软件基本功能要求如下:

程序可接收一个输入参数n,然后随机产生n道加减乘除(分别使用符号±*÷来表示)练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。

每个练习题至少要包含2种运算符。不得出现负数与非整数。

练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致。

当程序接收的参数为4时,以下为一个输出文件示例。

2018010203
13+17-1=29
11*15-5=160
3+10+4-16=1
15÷5+3-2=4
软件附加功能要求如下:(请有余力的同学完成)

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

扩展程序功能支持真分数的出题与运算(只需要涵盖加减法即可),例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6,且计算过程中与结果都须为真分数。(5分)
3.设计实现与PSP
1.输入控制
使用技术:正则表达式
2.随机数与随机符号的产生
使用技术:使用ArrayList和HashMap分别产出随机数与随机符号,利用Set的数据结 构特性保证产生符号的唯一性。
3.实现四则运算功能
使用技术:逆波兰式,栈式数据结构
4.文件的输入输出
5.PSP流程
|

任务内容 计划共完成需要的时间(min) 实际完成需要的时间(min)
计划
估计这个任务需要多少时间,并规划大致工作步骤
开发
需求分析 (包括学习新技术)
具体设计
具体编码
测试(自我测试,修改代码,提交修改)
测试报告并提交到Git
计算工作量
事后总结, 书写博客

4.算法详解
1.正则表达式
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
给定一个正则表达式和另一个字符串,我们可以达到如下的目的:
(1)给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”):
(2)可以通过正则表达式,从字符串中获取我们想要的特定部分。
(3) 正则表达式使用详解链接地址
2.Collection数据结构及其特性
数据结构按照逻辑关系(数据组织方式)主要划分为以下四类:
(1)集合
(2)线性结构
(3)树结构
(4)图结构
JDK中提供了Collection接口,用于定义一个聚合类应具有的基本功能(方法),而这些功能由接口的实现类完成。
在JDK1.4之后增加了Map接口,定义了映射集合类应具有的基本功能,并提供了散列映射集(HashMap)的实现。

2018102996+小学四则运算练习软件项目报告2018102996+小学四则运算练习软件项目报告
3.逆波兰式
(1)定义:逆波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)
(2)作用:实现逆波兰式的算法,难度并不大,但为什么要将看似简单的中序表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
(3)将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:

(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
(3)若取出的字符是“(”,则直接送入S1栈顶。
(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
(5)重复上面的1~4步,直至处理完所有的输入字符
(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。

a’c’n’k
完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!
参考文献:百度百科:逆波兰式
4.文件读写操作
5.Git的使用
Git常见命令:https://www.cnblogs.com/chenwolong/p/GIT.html
5.测试运行结果
2018102996+小学四则运算练习软件项目报告
6.代码实现

	Map<Integer, String> opeMap = new HashMap<Integer, String>();//使用HashMap
	opeMap.put(1, "+");
	opeMap.put(2, "-");
	opeMap.put(3, "*");
	opeMap.put(4, "÷");
	int opeNum = 0;
	ArrayList<String> operrayList = new ArrayList<String>();
	while(opeNum != nums - 1){//生成(随机数 - 1)个运算符
		Random randomOpe = new Random();
		int key = randomOpe.nextInt(4)+1;
		String ope01 = opeMap.get(key);
		operrayList.add(ope01);
		opeNum ++;
	}
    //  操作符栈
    private Stack<String> czf_stack = new Stack<>();        // 存放 运算符的栈
    private  ArrayList<String> ysbds_list = new ArrayList<>();     //存放 原始表达式的 arraylist
    private  ArrayList<String> nblbds_list = new ArrayList<>();      // 存放转换后的 逆波兰式
    private static final int One = 1;      //
    private static final int Two = 3;     //
    private static final int Three = 5;   //规定优先级   Three 最高
     //  定义一个运算栈
     private static Stack<String> ys_stack = new Stack<>();
     
      // 初始化                             使用StringTokenizer分割 字符串
     public Main(String bdString) {
         // TODO Auto-generated constructor stub
        StringTokenizer stringTokenizer = new StringTokenizer(bdString, "+-*÷()",true);
        while(stringTokenizer.hasMoreTokens()){
              ysbds_list.add(stringTokenizer.nextToken());
             //System.out.println(stringTokenizer.nextToken());
         }
    }
     
     /**
      * @author 李明阳
      * @title: isNum
      * @date 2016年3月27日 下午7:59:48
      * @param str
      * @return boolean
      */
     // 判断 是否是数字
     public boolean isNum(String str){
         if(str.matches("[0-9]+")){    //这里使用正则表达式 验证是否是数字
             //System.out.println("Y");
            return true;
         }else{
             //System.out.println("N");
             return false;
         }
     }
     
     // 判断 是否是操作符
    public boolean isCzf(String str){
        if(str.matches("[\\+\\-\\*\\÷\\(\\)]")){
            //System.out.println("Y");
            return true;
         }else{
             //System.out.println("N");
             return false;
         }
     }
    
     // 获取 优先级
    public int getYxj(String str){
         
         switch(str){
         case "(":return Three;
         case "*":
         case "÷":return Two;
         case "+":
         case "-":return One;
         case ")":return 0;
         
         default : return -1;
          
         }
         
      }
      /**
       * 
       * @author 李明阳
       * @title: isYxj
       * @date 2016年3月28日 上午9:00:02
       * @param str1
       * @param str2
       * @return boolean
       */
     // 判断优先级 
      public boolean isYxj(String str1,String str2){
          return getYxj(str1) > getYxj(str2);   
      }
      /**
       * 
       * @author 李明阳
       * @title: stack_czf
       * @date 2016年3月28日 上午9:01:12
       * @param czf void
       */
      //   ********* 当 当前操作元素为 操作符时**********    这里是 核心代码, 用于操作符栈的判断
      public void stack_czf(String czf){
         
          //判断当前栈内是否为空
        if(czf_stack.isEmpty()){
             czf_stack.push(czf);
              return;
         }
          
          //判断是否为 (
         if("(".equals(czf)){
             czf_stack.push(czf);
             return;
         }
         
         //判断是否为 )
         if(")".equals(czf)){
            String string = "";
             while(!"(".equals(string = czf_stack.pop())){
                 nblbds_list.add(string);
             }
             return;
        }
          
         //如果 当前栈顶元素是  (  直接入栈
         if("(".equals(czf_stack.peek())){
             czf_stack.push(czf);
             return;
         }
         
         // 判断 与 栈顶元素的优先级 , > 为true
         if(isYxj(czf, czf_stack.peek())){
            czf_stack.push(czf);
             return;
         }
         
         if(!isYxj(czf, czf_stack.peek())){
             nblbds_list.add(czf_stack.pop());
             stack_czf(czf);   //这里调用函数 本身,并将本次的操作数传参
         }
         
     }
     /**
      * 
      * @author 李明阳
      * @title: zz_hz
      * @date 2016年3月28日 上午9:28:44 void
      */
     // 中缀 —> 后缀
    public void zz_hz(){
         
         // 遍历原始表达式list
         for(String str:ysbds_list){
             
             //System.out.println("->  " + str);
             
             if(isNum(str)){
                 nblbds_list.add(str);
             }else if(isCzf(str)){
                 stack_czf(str);
             }else{
                 System.out.println("非法");
             }
             
         }
         
         // 遍历完原始表达式后  将操作符栈内元素 全部添加至 逆波兰表达式list
 
         while(!czf_stack.isEmpty()){
             //System.out.println("即将 " + czf_stack.peek());
            nblbds_list.add(czf_stack.pop());
         }
         
     }
     /**
      * 
      * @author 李明阳
      * @title: jsff
      * @date 2016年3月28日 上午10:03:01
      * @param s1
      * @param s2
      * @param s3
      * @return int
      */
    // 具体计算方法
     public Float jsff(String s1,String s2,String s3){
         float a = Float.parseFloat(s2);
         float b = Float.parseFloat(s1);
         switch(s3){
         case "+":return a+b;
         case "-":return a-b;
         case "*":return a*b;
         case "÷":return a/b;
         default : return (float) 0.0;
        }
     }
     /**
      * 
      * @author 李明阳
      * @title: js_nbl
      * @date 2016年3月28日 下午3:21:41
      * @return flaot
      */
     //  计算 逆波兰式
     public Float js_nbl(){

7.总结