四则运算生成程序

by 叶尚文 and 张鸿

GitHub地址:https://github.com/MansonYe/Calculate


一.需求分析

  1. 使用-n 参数控制生成题目的个数

2. 使用-r 参数控制题目中数值(自然数、真分数和真分数分母)的范围

3. 生成的题目中计算过程不能产生负数

4. 生成的题目中如果存在形如e1 / e2的子表达式,那么其结果应是真分数

5. 每道题目中出现的运算符个数不超过3个

6. 程序生成的题目不能重复,任何两道题目不能通过交换律变换为同一道题目

7. 生成题目存入程序当前目录下Exercises.txt文件

  答案存入程序当前目录下Answers.txt文件

8. 程序应能支持一万道题目的生成

9. 参数“-e”“-a”对给定的题目文件和答案文件,判定对错并进行数量统计

可见,功能可分为几大类:

1.调用其他类函数进行文件读写的主类

2. 随机生成后缀式并调节成中缀式的创建类

3. 对后缀式进行计算并判断结果和计算过程是否符合要求的计算类

4. 把读入程序的算式转换为后缀式的转换类(用于需求9)

二. PSP2.1表格

三、设计实现过程:

主类:

  在主类调用另外三个大类的函数完成程序的功能:

 

创建类:

  先随机创建后缀式算式;

  然后转换为中缀式;

  并暂存生成的后缀式及中缀式。

计算类:

  读取后缀式,以栈的形式完成计算,生成结果的分子和分母;

  判断结果是否符合要求;

  把符合要求的结果的分子分母转换为真分数

转换类:

因为中缀式转后缀式比较麻烦,故分出一个类完成这项操作;

后缀式栈 CaluBack存储后缀式

运算符栈 Operate 存储运算符

Priority和compare方法用来比较运算符优先级

先将中缀表达式字符串转换成字符数组

然后判断是否为运算符

       若为操作数则count自增1

若为运算符,则先将前一个操作数存入后缀式栈

若当前运算符为括号或优先级高于Operate栈顶则另做处理

否则将该运算符存入Operate栈

将字符数组中和Operate栈中剩余元素分别存入后缀式栈中

四、代码说明:

主类:

Build方法:

 1 private void Build(int Range, int Number) {
 2         CreateClass Create = new CreateClass();
 3         CountClass Count = new CountClass();
 4
 5         FileOutputStream outputStream1 = null;
 6         PrintWriter printWriter1 = null;
 7         FileOutputStream outputStream2 = null;
 8         PrintWriter printWriter2 = null;
 9         int i = 1;
10
11         String[] CaluBack;//暂存后缀式
12         String Calu;//保存中缀式
13         String Result;//保存结果
14
15         try {
16             outputStream1 = new FileOutputStream("Exercises.txt");
17             printWriter1 = new PrintWriter(outputStream1);
18             outputStream2 = new FileOutputStream("Answers.txt");
19             printWriter2 = new PrintWriter(outputStream2);
20
21             do {
22                 CaluBack = Create.CaluCreate(Range);//创建后缀式,并保存(字符数组)
23                 Create.EquationConstruct();//转换为中缀式
24
25                 if(Count.CaluCount(CaluBack)) //根据后缀式计算结果,返回值表示计算结果是否符合要求
26                 {
27                     Result = Count.getResult();//保存结果(字符串)
28                     Calu = Create.getCalu();//保存中缀式(字符串)
29
30                     printWriter1.println(i + ". " + Calu);
31
32                     printWriter2.println(i + ". " + Result);
33
34                     i++;
35
36                 }
37                 else {//不符合要求,则Number自加,以便重做
38                     Number++;
39                 }
40
41             }while(--Number != 0);
42
43
44         } catch (IOException e) {
45             System.out.println("Sorry, there has been a problem opening or writing to the file!");
46         } finally {
47             if(printWriter1 != null) {
48                  printWriter1.close();
49             }
50             if(printWriter2 != null) {
51                  printWriter2.close();
52             }
53         }
54
55     }

Judge方法:

 1 private void Judge() {
 2         File fileE = new File("Exercises.txt");
 3         File fileA = new File("Answers.txt");
 4         BufferedReader readerE = null;
 5         BufferedReader readerA = null;
 6         Scanner input = new Scanner(System.in);
 7         int Tnum = 0;
 8         int Fnum = 0;
 9         int Anum = 1;
10         String True = "";
11         String False = "";
12
13         try {
14
15            readerE = new BufferedReader(new FileReader(fileE));//reader打开文件内容
16            readerA = new BufferedReader(new FileReader(fileA));
17
18            String LineE = "";
19            String LineA = "";
20
21            LineE = readerE.readLine();//do first
22            LineA = readerA.readLine();
23
24            while(LineE != null){
25                System.out.print(LineE + " = ");
26
27                String temp = (Anum++) + ". " + input.nextLine();
28                if(LineA.equals(temp)) {//读入答案,并判断对错
29                    System.out.println("True");
30                    Tnum++;
31                }
32                else {
33                    System.out.println("False");
34                    Fnum++;
35                }
36
37                LineE = readerE.readLine();//do again
38                LineA = readerA.readLine();
39            }
40
41            readerE.close();
42            readerA.close();
43            if(True != "")
44                 True = True.substring(0, (True.length()-2) );
45             if(False != "")
46                False = False.substring(0, (False.length()-2) );
47             System.out.println("\nCorrect:  " + Tnum + " (" + True + ")");
48             System.out.println("Wrong:  " + Fnum + " (" + False + ")");
49
50
51         } catch(IOException e) { e.printStackTrace(); }
52
53         input.close();
54     }

Compare方法:

private void Compare(String ExeFileAddress, String AnsFileAddress) {//比较answer文档和正确答案File fileE = new File(ExeFileAddress);File fileA = new File(AnsFileAddress);BufferedReader readerE = null;BufferedReader readerA = null;Transform caltest = new Transform();CountClass Count = new CountClass();int Anum = 0;int Tnum = 0;int Fnum = 0;String True = "";String False = "";String[] CaluBack = new String[7];//后缀式
        String Result;try {readerE = new BufferedReader(new FileReader(fileE));//reader打开文件内容readerA = new BufferedReader(new FileReader(fileA));String LineE = "";String LineA = "";LineE = readerE.readLine();//do firstLineA = readerA.readLine();while(LineE != null){ Anum++;LineE = LineE.substring((Anum+"").length()+2);caltest.prepare(LineE);//中缀转后缀CaluBack = caltest.getPostfixStack();Count.CaluCount(CaluBack);Result = Count.getResult();if(LineA.equals(Anum + ". " + Result)) {//读入答案,并判断对错True += Anum + ", ";Tnum++;}else {False += Anum + ", ";Fnum++;}LineE = readerE.readLine();//do againLineA = readerA.readLine();}readerE.close();readerA.close();if(True != "")True = True.substring(0, (True.length()-2) );if(False != "")False = False.substring(0, (False.length()-2) );System.out.println("\nCorrect:  " + Tnum + " (" + True + ")");System.out.println("Wrong:  " + Fnum + " (" + False + ")");} catch(IOException e) { e.printStackTrace(); } }

创建类:

import java.util.Stack;public class CreateClass {private String[] CaluBack = new String[7];private String Calu = "";private boolean isDigit(String strNum){return strNum.matches("[0-9]{1,}");}public String[] CaluCreate(int Rance) {    //创建一个逆波兰式的算式String[] SingCollection = {"+", "-", "*", "/", "#"};int n = 0;int c = 0;int i=0;do {if( ((int)(Math.random()*2) != 1) && (n-1 > c) ) {//随机但后缀式中算符数量不可大于数字数量(从左到右)CaluBack[i++] = SingCollection[(int)(Math.random()*(5 - c/2))];c++;}else if(n < 4){//生成4个数字了就不再生成数字CaluBack[i++] = (int)(Math.random()*Rance) + "";n++;}}while(n+c < CaluBack.length);return CaluBack;}public String EquationConstruct() {    //后缀式转中缀式Stack<String> CaluMid = new Stack<String>();int loop1;String s1, s2;for(loop1=0; loop1<CaluBack.length; loop1++) {//注释参考隔壁CountClassif(isDigit(CaluBack[loop1])) {CaluMid.push(CaluBack[loop1] + "");}else { s1 = CaluMid.pop();s2 = CaluMid.pop();switch(CaluBack[loop1]) {case "+": CaluMid.push(s2 + " + " + s1); break;case "-": CaluMid.push(s2 + " - " + s1); break;case "*": CaluMid.push(s2 + " * " + s1); break;case "/": CaluMid.push(s2 + " / " + s1); break;case "#": CaluMid.push(s2); break;default: break;}if(loop1 != CaluBack.length - 1 && CaluBack[loop1] != "#") {CaluMid.push("(" + CaluMid.pop() + ")");}}    }Calu = CaluMid.pop();return Calu;}public void CreateTest() {    //测试for(int i=0; i<CaluBack.length; i++)System.out.print(CaluBack[i]);System.out.println("\n" + Calu);}public String getCalu() {return Calu;}public String[] getCaluBack() {return CaluBack;}}

计算类:

public class CountClass {private String Result = "";private boolean isDigit(String strNum){return strNum.matches("[0-9]{1,}");}public boolean CaluCount(String[] CaluBack) {int loop1 = 0;//循环后缀式int loop2 = 0;//模仿栈int mole = 0;//分子int deno = 0;//分母int d1, d2, m1, m2;Elem[] num = new Elem[4];while(loop2 < 4)num[loop2++] = new Elem();loop2 = 0;for(loop1 = 0; loop1 < CaluBack.length && CaluBack[loop1] != null; loop1++) {if( isDigit(CaluBack[loop1]) ) {//判断是否为数字,目前只做了个位数判断,后期视情况修改num[loop2].setMole( Integer.parseInt(CaluBack[loop1]) * num[loop2].getDeno() );loop2++;//利用num[loop2]模仿栈,把数字放入栈中((int)-48)
            }else {//是算符的话,根据情况作计算
                m1 = num[loop2-2].getMole();//取出伪栈中2个数据进行计算m2 = num[loop2-1].getMole();d1 = num[loop2-2].getDeno();d2 = num[loop2-1].getDeno();switch(CaluBack[loop1]) {case "+": mole = m1 * d2 + m2 * d1;    deno = d1 * d2;    break;case "-":mole = m1 * d2 - m2 * d1;    deno = d1 * d2;if(mole < 0)//防止计算过程中出现负数return false;break;case "*": mole = m1 * m2;    deno = d1 * d2;    break;case "/": mole = m1 * d2;    deno = d1 * m2;    break;case "#": mole = m1;    deno = d1;    break;default: break;}num[loop2-2].setDeno(deno);//计算结果入栈num[loop2-2].setMole(mole);num[loop2-1] = new Elem();//清空出栈元素位置loop2--;}//end else}//end forif(num[0].getDeno() <= 0 || num[0].getMole() < 0) {//如果结果不符合要求返回falsereturn false;}else {    ResultConstruct(num[0]);//符合要求则将假分数转换为真分数
        }return true;}private void ResultConstruct(Elem elem) {//将假分数转换为真分数,并存入Resultint mole = elem.getMole();int deno = elem.getDeno();int Integer = mole/deno;int a = mole;//a, b and c are used to count divisorint b = deno;int c = 0;mole = mole % deno;Result = "";if(mole == 0)Result = Integer + ""; else { while(a % b != 0) {c = a % b;a = b;b = c;}mole /= b;deno /= b;if(Integer == 0)Result = mole + "/" + deno;else    Result = Integer + "'" + mole + "/" + deno;}}public String getResult() {return Result;}public void CountTest() {System.out.println(Result);}}

转换类(针对需求9)

import java.util.Collections;
import java.util.Stack;public class Transform {private Stack<String> CaluBack = new Stack<String>();// 后缀式栈private Stack<Character> Operate = new Stack<Character>();// 运算符栈private int[] priority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };//分别对应( ) * + , - . /private boolean isOpera(char c) {return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';}private boolean isOperaS(String c) {return c == "+" || c == "-" || c == "*" || c == "/" || c == "(" || c == ")";}private boolean isDigit(String strNum){return strNum.matches("[0-9]{1,}");}public boolean compare(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低return priority[(peek) - 40] >= priority[(cur) - 40];}public String[] getPostfixStack() {String[] CaluBacktest = new String[7];String Temp;int i = 0;Collections.reverse(CaluBack);while(!CaluBack.isEmpty()) {Temp = CaluBack.pop().trim();if(!Temp.isEmpty()) {CaluBacktest[i] = Temp;i++;}}return CaluBacktest;}public void prepare(String expression) {Operate.push(',');// 运算符放入栈底元素逗号,此符号优先级最低char[] arr = expression.toCharArray();int location = 0;// 当前字符的位置int count = 0;// 两次算术运算符的字符的长度char currentOp, topOp;// 当前操作符和栈顶操作符for (int i = 0; i < arr.length; i++) {currentOp = arr[i];if (isOpera(currentOp)) {//如果当前字符是运算符if (count > 0) {CaluBack.push(new String(arr, location, count));// 取两个运算符之间的数字
                }topOp = Operate.peek();if (currentOp == ')') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号while (Operate.peek() != '(') {CaluBack.push(String.valueOf(Operate.pop()));}Operate.pop();} else {while (currentOp != '(' && topOp != ',' && compare(currentOp, topOp)) {CaluBack.push(String.valueOf(Operate.pop()));topOp = Operate.peek();}Operate.push(currentOp);}count = 0;location = i + 1;} else {//否则间距自加count++;}}if (count > 1 || (count == 1 && !isOpera(arr[location]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中CaluBack.push(new String(arr, location, count));}while (Operate.peek() != ',') {CaluBack.push(String.valueOf(Operate.pop()));// 将操作符栈中的剩余的元素添加到后缀式栈中
        }}}

五、测试结果:

以Eclipse中单元测试功能作测试

 1 import static org.junit.Assert.*;
 2 import org.junit.Test;
 3
 4 public class test1 extends MainClass{
 5
 6     @Test
 7     public void test() {
 8         System.out.println("test");
 9         MainClass testMain = new MainClass();
10         String[] test1 = {"-n", "5", "-r", "10"};
11         String[] test2 = {"-a", "answersfile.txt", "-e", "exercisesfile.txt"};
12         String[] test3 = {"-n", "10000", "-r", "10"};
13
14         testMain.main(test1);
15     }
16
17 }

基础功能(test1):

需求9(test2):

压力测试(test3)仅生成测试(加上输入怕是要按坏键盘):

六、小结:

  这次项目比上次复杂,而且自己也挺久没用java了,生疏之余,忘记了许多应该注意的细节,更没有想到的事需求9的难度比想象中的难度大。

  这次的结对编程还是学到了很多东西,例如一开始构思的时候,我就打算直接生成一条从左到右的线性算式,然后再加工,从而无视后缀式、中缀式等结构转换问题,但同学的反对虽然使起步的速度降低了,但在后续编程方面,后缀式的引入无疑给程序提供了极大的便利。此外,虽然同学没怎么打代码,但是,他利用丰富的知识储备和多样的学习方法(baidu)帮助我攻克了不少bug,再次感谢牺牲打游戏时间帮我debug的同学。

  那么!为什么又拖到那么晚交呢?我也不想熬夜啊!看了眼现在都是在打dnf的合作伙伴。。。emmmmm

  原因可能是博文太难写吧(主类的图我画了半天)

转载于:https://www.cnblogs.com/mansonye/p/9727312.html

四则运算 - java实现(叶尚文, 张鸿)相关推荐

  1. 四则运算java_小学生四则运算JAVA

    组员:黄浩格,何坤 一.项目说明 1题目:实现一个自动生成小学四则运算题目的命令行程序. 2说明: 自然数:0, 1, 2, -. • 真分数:1/2, 1/3, 2/3, 1/4, 1'1/2, - ...

  2. Java支持latex,基于Java和LaTeX的文档自动生成技术研究

    基于Java和 LaTeX 的文档 自动生成技术研究 ◆尚宝欣 徐 屹 (东北电力大学理学院,吉林 长春 132012) [摘 要]讨论了结合Java与LaTex 自动生成 PDF文档的方法.针 展名 ...

  3. 【Java实现导出Word文档功能 XDocReport +FreeMarker】

    Java实现导出Word文档功能(XDocReport +FreeMarker) 前言 在日常的开发工作中,我们时常会遇到导出Word文档报表的需求,比如公司的财务报表.医院的患者统计报表.电商平台的 ...

  4. Java实现点击中文文字图片验证码

    Java实现点击中文文字图片验证码 环境条件 JDK1.8 MAVEN-3.3 spring-boot-2.1.17.RELEASE Redis 注意事项 Java AWT在背景图片上绘写文件时候设置 ...

  5. Realm(Java)数据库使用文档(查询Queries)

    文章目录 8.1 筛选 8.2 逻辑运算符 8.3 排序 8.4 结果限制 8.5 唯一值 8.6 链式查询(Chaining queries) 8.7 关联查询(Link queries) 8.8 ...

  6. 关于用java编写生成word文档,动态添加数据到word文档的一些心得

    关于用java编写生成word文档,动态添加数据到word文档的一些心得,经过翻阅了无数的有用的和无用的资料以后,总算找到了一种靠谱的方法 1.概述 经过反反复复的查阅资料,总算找到了一个靠谱的生成w ...

  7. 张鸿轩:大数据让无形之风尽在掌握 | 优秀毕业生专访

    [ 导读 ]清华-青岛数据科学研究院(以下简称"数据院")自2014年4月成立以来,秉承"学校统筹,问题引导,社科突破,商科优势,工科整合,业界联盟"的指导原则 ...

  8. 炫界 (302) -(查动简)_原302张鸿飞主任——乙肝抗病毒治疗常见的问题与难点

    抗病毒治疗是慢性乙型肝炎治疗的关键.但在临床诊疗过程中,由于乙肝病毒复制本身的特性.病毒变异.人体免疫系统对乙肝病毒的免疫耐受以及患者认识上的不足等诸多因素,导致诸多抗病毒治疗问题和难点.(原302张 ...

  9. 【Java NIO】一文了解NIO

    [Java NIO]一文了解NIO Java NIO 1 背景介绍 在上一篇文章中我们介绍了Java基本IO,也就是阻塞式IO(BIO),在JDK1.4版本后推出了新的IO系统(NIO),也可以理解为 ...

最新文章

  1. 用户管理之用户的查询获取
  2. CISCO PIX/ASA Failover 技术初步学习
  3. ZCMU 1894: Power Eggs
  4. 【干货】Oracle数据库常用十一大操作指令
  5. WebDriver API元素的定位
  6. 单载波DSP模块介绍
  7. 开题报告方案论证_【实验科研】我校五项省教育规划教研专项重点课题开题
  8. 码农们的聚餐,会复杂到什么程度?
  9. Flutter之路由系列之Navigator简析
  10. EtherCAT总线伺服速度控制功能块(H5U PLC)
  11. 解决origin8 licience 过期问题
  12. linux+gps测试流程图,卫星导航产品(GPS)测试方案详解
  13. word如何一次将所有英文改为新罗马字体
  14. Java Excel文件内容替换
  15. 微信自媒体账号涉违规大规模被封
  16. 挖洞经验 | 利用Slack应用程序窃取Slack用户的下载文件
  17. android_day01
  18. python抓取谷歌指数(Google Trends)
  19. php 命格算法,命格是怎么个算法???
  20. jmeter接口自动化,你敢想,我敢玩

热门文章

  1. 计算机应用基础浙大,2014春浙大远程教育计算机应用基础-3.Word知识题
  2. 管家婆有未记账的凭证怎么办_管家婆常见问题
  3. 网络推广期间发现网站快照更新过慢会影响正常网络推广吗?
  4. 网站推广收录少?网站推广专员浅析可能是蜘蛛抓取出现问题
  5. 企业网络推广——企业网络推广专员要学会打开网站优化新思路
  6. 搜索引擎蜘蛛为什么对网站不爬行呢?
  7. 网站内容优化——什么样的文章可以帮助提升网站优化水平呢?
  8. 怎么查询redis缓存的数据_阿里开发十年写出这份「Redis简明教程」+「Redis实战」请你查收...
  9. php excel 组件,Yii Framework框架使用PHPExcel组件的方法示例
  10. plsq如何快捷整理代码_PLSQL Developer使用技巧整理(转)