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

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

 

写在前面:第一次看到作业要求时,觉得很难,一开始有种抗拒的心态。自己一直以来学习前端,对后端只是基本的了解而已。写的过程遇到了很多问题,虽然项目中还有许多不完善的地方,但通过此次作业,一个一个解决问题。经过这些天的“补课”,让我加深了后端的学习,虽然辛苦,但是收获了很多(所以说欠的债总是要还的)。

项目克隆地址:https://git.coding.net/chenxin1998/calculate2.git

一.需求分析

小学生计算能力需要通过练习大量的运算题目得到锻炼,因此很多老师让家长每天给学生出相应的练习题。为了方便老师的教学任务,同时减轻家长的负担,现在需要一个可以出四则混合运算的小型软件。

1.通过程序接受一个数字,产生N道加减乘除练习题。

2.判断传入参数是否合法

3.每个数字在0-100之间,运算符3-5个

4.运算过程不能出现负数和非整数

5.计算出练习题结果

6.所有信息输出到txt文件

二.功能设计

基础功能:

  1. 程序可接收一个输入参数n,然后随机产生n道加减乘除练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。
  2. 为了让小学生得到充分锻炼,每个练习题至少要包含2种运算符。同时,由于小学生没有分数与负数的概念,你所出的练习题在运算过程中不得出现负数与非整数,比如不能出 3/5+2=2.6,2-5+10=7等算式。
  3. 练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致。
  4. 当程序接收的参数为4时,以下为输出文件示例。 

     

扩展功能:

  1. 支持有括号的运算式,包括出题与求解正确答案。注意,算式中存在的括号必须大于2个,且不得超过运算符的个数。
  2. 扩展程序功能支持真分数的出题与运算,例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6。

三.设计实现:

1.在实现过程中,为了更好地实现模块化,我分了三个类,分别是:

CreatProblem.java(随机创建一个运算过程不含负数和小数的四则算式);RandomArithmetic.java(主程序类操作,但操作隐藏);WriteResult.java(文件写入)

2.用到的重要函数:

产生四则运算:CreateProblem函数,其中Random随机产生3-5个运算符,且带括号的四则运算式子。若式子不符合要求,则用递归到合法为止。

计算结果:getResult函数,在四则运算式生成后,计算每一部分的结果,且排除带有小数和负数的式子。

启动程序:RandomAtithmetic函数,接收参数n,并判断其合法性。

3.用两个Character栈存储数字和符号,然后用两个栈分别用作转换的缓冲栈和计算结果的缓冲栈

4.一般Character里面的编码是ascii,但我没有那样做,就是一种新的编码,用ascii0-99表达0-99,而符号和数字冲突,所以符号是100-105

优点:比用string省空间

缺点:虽然有括号识别但随机生成算式没有括号(这是需要继续完善的地方)

 

 

 

四.测试运行:

进入src文件下,输入javac -encoding utf-8 RandomArithmetic.java 编译出相应的class文件,再输入java RandomArithmetic 10进行测试:

 

 

注意:一开始进行测试的时候,在命令行会出现“找不到或无法加载主类”的情况。最后通过查阅博客,发现是由于JRE版本问题。这是由于我使用高版本的JDK编译的Java class文件试图在较低版本的JVM上运行,所报的错误。 因为,高版本的JDK生成的class文件使用的格式,可能与低版本的JDK的.class文件格式不同。这样,低版本的JVM无法解释执行这个.class文件。于是,问题就变成了“如何解决JDK和JRE版本不一致”,最后只是通过从新安装JDK和JRE,然后重配环境解决。

里面还有一些原理没有弄懂,待以后开发。

总结几个知识点吧~~

JDK、JRE有什么区别:

Jre   是java   runtime   environment的缩写,   是java程序的运行环境。既然是运行,当然要包含jvm,也就是大家熟悉的虚拟机啦, 还有所有java类库的class文件,都在lib目录下打包成了jar。大家可以自己验证。至于在windows上的虚拟机是哪个文件呢?  Java\jdk\ jre\bin\client里面是不是有一个jvm.dll呢?那就是虚拟机。

Jdk   是java   development   kit,是java的开发工具包,里面包含了各种类库和工具。当然也包括了另外一个Jre--------Java\jre.  那么为什么要包括另外一个Jre呢?而且jdk\jre\bin同时有client和server两个文件夹下都包含一个jvm.dll。   说明是有两个虚拟机的。

Java\jdk\bin这个bin下有各种java程序需要用到的命令,与Java\jdk\jre\bin或者Java\jre\bin的bin目录最明显的区别就是Java\jdk\bin下才有javac.exe,这一点很好理解,因为 jre只是一个运行环境而已。与开发无关,正因为如此,具备开发功能的jdk自己的jre下才会同时有client性质的jvm和server性质的 jvm, 而仅仅作为运行环境的jre下只需要client性质的jvm.dll就够了。

我们用的java命令并不是 Java\jdk\bin目录下的而是Java\jdk\jre\bin目录下的。不信可以做一个实验,大家可以把Java\jdk\bin目录下的java.exe剪切到别的地方再运行 java程序,发现了什么?一切OK!

如果java为了提供给大多数人使用,他们是不需要jdk做开发的,只需要jre能让java程序跑起来就可以了,那么每个客户还需要手动去设置环境变量多麻烦啊?

所以安装jre的时候安装程序自动帮你把jre的java.exe添加到了系统变量中,因此去C:\Windows\system64下面去看看吧,发现有一个java.exe。

 五.核心代码:

(1)JAVA用栈处理四则运算:

一共分两步:

1.中缀表达式转后缀表达式

从左到右遍历中缀表达式的每一个数字和运算符。
如果数字就输出(即存入后缀表达式);
如果是右括号,则弹出左括号之前的运算符;
如果优先级低于栈顶运算符,则弹出栈顶运算符,并将当前运算符进栈。
遍历结束后,将栈则剩余运算符弹出。

2.后缀表达式表达计算结果

从左到右遍历后缀表达式,遇到数字就进栈,遇到符号,就将栈顶的两个数字出栈运算,运算结果进栈,直到获得最终结果。

 

//创建若干个四则运算中缀表达式
//////////////////////////////////////
///注意,为减小内存使用,ascii0-100表示数字0-99,100-103分别表示加减乘除,()为104,105
////////////////////////////////////////
import java.util.Random;
import java.util.*;//
import java.lang.*;

public class CreateProblem {
    private int numOfNum=3;//存储该表达式有几个运算数,默认3个
    private Stack<Character> infix= new Stack<Character>();//中缀表达式  
    private Stack<Character> suffix=new Stack<Character>();//后缀表达式
    private Stack<Character> stackOfOperation=new Stack<Character>();//计算后缀表达式所用栈
    private Stack<Integer> stackOfNum=new Stack<Integer>();//计算结果所用栈
    private int max=100;//运算数最大值,默认100

 (2)如何解决四则运算表达式中不能出现负数和小数的问题

int getResult()//如果结果是-1,说明随机运算不满足要求,重新创建新的运算式
	{
		//根据后缀表达式计算并且检查算式是否合法,是否有小数或者负数在运算中出现,如果出现,重新创建新的表达式并相应转换
		int i=0;
		while(i<suffix.size())
		{
			int temp,temp0;
			char c=suffix.get(i);
			if(isDigit(c))  stackOfNum.push((int) c);//将所有字符数字转换为整数型并入栈
			else if(c==100||c==101||c==102||c==103)
			{
				switch(c)
				{
				case 100:
					temp=stackOfNum.pop();
					temp0=stackOfNum.pop();
					stackOfNum.push(temp0+temp);
					break;
					
				case 101:
					temp=stackOfNum.pop();
					temp0=stackOfNum.pop();
					if(temp0-temp<0) return -1;//不满足运算中不得出现负数的要求,因此结束程序
					else stackOfNum.push(temp0-temp);
					break;
				case 102:
						temp=stackOfNum.pop();
						temp0=stackOfNum.pop();
						stackOfNum.push(temp*temp0);
						break;
				case 103:
						temp=stackOfNum.pop();
						temp0=stackOfNum.pop();
						if(temp0%temp==0) stackOfNum.push(temp0/temp);
						else return -1;//不满足运算中不得出现小数的要求,因此结束程序
						break;
				}
			}
			i++;
		}
		if(!stackOfNum.empty()) return stackOfNum.pop();
		else return -1;
	}

 (3)一开始在写文件的时候出现了陷入循环无法出来的情况,错误代码如下:

public static void write(CreateProblem problem) throws IOException
	{
		int i=0;
		File file=new File("result.txt");
		try (PrintWriter output=new PrintWriter(file);
		)
		{
		while(i<problem.showInfix().size()) 
		{
			String str=new String("");
			char ch=problem.showInfix().get(i);
			switch(ch)
			{
			case 100:str+"+";break;
			case 101:str+"-";break;
			case 102:str+"*";break;
			case 103:str+"/";break;
			case 104:str+"(";break;
			case 105:str+")";break;
			default:str+((int)ch);break;
			}
			i++;
		}
		output.print('=');
		output.print(problem.getResult());
		output.print('\n');
		}
		file.close();
		output.close();
	}
	public static void write(String s) throws IOException
	{
		File file=new File("result.txt");
		try (PrintWriter output=new PrintWriter(file);
			
		) {
		output.println(s);
		}
		file.close();
		output.close();
	}
}

修改后的代码如下:

import java.io.*;
//写文件
public class WriteResult
{

	public static void w1(String str) {
		
	    try {
	    File f=new File("result.txt");
	    FileWriter writer=new FileWriter(f,true);
	    PrintWriter pw=new PrintWriter(writer);
	    pw.append(str);
	    pw.println("");
	    pw.flush();
	    pw.close();
	    } catch (IOException e) {
	      e.printStackTrace();
	    }
	}
    
	public static void w2(CreateProblem problem)
	{
		int i=0;
		String s=new String();
		while(i<problem.showInfix().size()) 
		{
			char ch=problem.showInfix().get(i);
			switch(ch)
			{
			case 100:s+="+";break;
			case 101:s+="-";break;
			case 102:s+="*";break;
			case 103:s+="/";break;
			case 104:s+="(";break;
			case 105:s+=")";break;
			default:s+=((int)ch);break;
			}
			i++;
		}
		 try {
			    File f=new File("result.txt");
			     FileWriter writer=new FileWriter(f,true);
			     PrintWriter pw=new PrintWriter(writer);
			      pw.append(s);
			      pw.append('=');
			      pw.println(problem.getResult());
			      pw.flush();
			      pw.close();
			    } catch (IOException e) {
			      e.printStackTrace();
			    }
	}
}

 

六.总结:

在该小学四则运算小程序中主要分为三个类,其中RandomArithmetic类负责开启程序运行的入口,CreateProblem主要用于处理运算式,WriteResult用于文件写入,整个程序由这三个类块构成,实现了功能的模块化。其中在CreateProblem类中又分离出了private Stack<Integer> stackOfNum=new Stack<Integer>();//计算结果所用栈;private Stack<Character> suffix=new Stack<Character>();//后缀表达式private Stack<Character> stackOfOperation=new Stack<Character>();//计算后缀表达式所用栈;这样设计使得各功能再次模块化,使得程序更加的便于维护和扩展。

最后,就是完成一个项目的喜悦了,所以从事我们这一行,还是要多看,多写,理论与实践结合。

 七.PSP总结

PSP2.1

任务内容

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

实际完成的时间(h)

Planning

计划

 0.5  1

·   Estimate

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

 3天  4天

Development

开发

 2天  3天

··  Analysis

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

 12  18

·   Design Spec

·生成设计文档

 0.5  0.5

·   Design Review

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

 0.6  0.8

·   Coding Standard

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

 0.5  0.6

·   Design

  具体设计

 3  3.5

·   Coding

  具体编码

 12  20

·   Code Review

· 代码复审

 3  4

·   Test

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

 2  5

Reporting

报告

 3.5 4

··  Test Report

· 测试报告

 1.5  2

·   Size Measurement

  计算工作量

 0.5  0.5

·  Postmortem & Process Improvement Plan

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

 1.5  1.5
posted on 2018-03-25 12:05  陈欣cc  阅读(207)  评论(2编辑  收藏  举报