项目成员:黄思扬(3117004657)、刘嘉媚(3217004685)

二、PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)

Planning

计划

60

40

· Estimate

· 估计这个任务需要多少时间

60

40

Development

开发

1440

1505

· Analysis

· 需求分析

30

15

· Design Spec

· 生成设计文档

20

15

· Design Review

· 设计复审

30

15

· Coding Standard

· 代码规范

20

20

· Design

· 具体设计

80

80

· Coding

· 具体编码

900

980

· Code Review

· 代码复审

30

30

· Test

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

330

400

Reporting

报告

130

100

· Test Report

· 测试报告

80

60

· Size Measurement

· 计算工作量

30

20

· Postmortem & Process Improvement Plan

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

30

20

合计

1630

1695

三、效能分析

由于之前采用单线程执行,在文件IO流的处理上花费了不少的时间,包括代码上的执行存在部分冗余,代码上可以提高利用率。打开了线程池以后,多线程执行,大大提高了执行速度,在代码逻辑改进优化后,对大量生成题目的效果十分显著,由30s时间完成优化到2s:

四、设计过程

(一)流程图

视图设计过程:

生成题目设计过程:

判断对错设计过程:

(二)项目目录:

分包思路:

1)视图层:view 包含主页面与两个功能页面

2)实体类:po  包含题目存放类Deposit、ChildDeposit,分数处理类

3)逻辑处理层:service  处理题目生成等的逻辑、处理题目判错的逻辑

4)工具类:util 包含文件的读写功能

(三)总体实现思路

程序运行,进入主页面,点击选择进入相应功能页面(生成题目or判断对错),如果为生成题目,用户需要输入相关参数(题目数量、数的大小范围),视图层获取输入的数据,传入至逻辑层中进行处理,先判断输入是否有误,有误则终止程序,无误则调用题目生成的方法CreateAth;如果为判断对错,用户需要选择相应的文件(Exersises.txt和Answers.txt),视图层获取输入的数据,传入到逻辑层进行处理,判断输入无误后,调用题目判错的方法Judge。

题目生成的思路:题目要求生成的算术运算符少于3个,先随机生成一个运算符的个数,传入到CreateAth方法中,先生成一个根结点即算术运算符,然后随机生成在左右子树小于总运算符个数的运算符个数,同时生成运算符,当生成运算符个数为0,则生成叶子结点即运算操作数,左右子树也是重复上述过程。把生成的算式放在一个动态数组里面,每当生成一个算式,就去检查里面是否包含这个算式,如果重复就去掉,达到查重的目的。

判断对错的思路:使用readfile读取Exersises.txt文件内容,使用正则表达式分割开题目的序号和算式,算式是中缀表达式的表示方式,通过build方法把算式改为前缀表达式,如:1 + (( 2 + 3)* 4 ) – 5,转换成前缀则为- + 1 * + 2 3 4 5,计算其答案,读取Answers.txt的内容,使用正则表达式分割开题目的序号和答案,根据两个文件的序号,对比答案是否相同,如果相同则记录对的题目的数量,和题目序号,写出到Correction.txt文件。

(四)细节实现思路

1)如何保证基本的数值运算,确定参数的范围?

自然数的数值之间的运算可简单实现,但是自然数、真分数、带分数之间的运算之间的格式需要自己设计的,并且题目要求“如果存在形如e1÷ e2的子表达式,那么其结果应是真分数”,经过讨论之后,决定把所有数据统一当成分数来处理,整数的分母则为1,在运算的过程中把分子与分母独立出来分别操作加减乘除运算,到最后再进行约分等化简处理。

2)怎么生成算式并且查重?

生成的算式要求不能产生负数、生成的题目不能重复,且即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目,经过讨论,决定使用二叉树来实现,由于二叉树的运算次序是孩子结点的运算次序要优先于根结点的,所以使用非叶子节点存放运算符,叶子结点存放数值,可以解决前后符号之间的优先级别关系,解决括号的添加问题,当父母结点的算术符优先级高于右孩子结点的算术运算符时,左右都要加括号,当相等时,则右孩子树要加括号;出现了负数时,交换左右子树即可;此外,解决了查重的复杂度问题,开始的方案想采用遍历的方式来达到查重,现只需要判断两棵树是否相同即可。

五、程序关键代码

从页面获取到数据,进行处理:

packageservice;importjava.util.HashMap;importjava.util.Map;public classEntryJudge{public booleanEntry(String[] args){//向主页面返回的运行成功与否的标志

boolean tag = false;//判断用户输入是否正确

if(args.length == 0 || args.length % 2 != 0) {

tag= false;returntag;

}//取出参数

Map params =checkParams(args);//执行相应处理

CreateAth opera = newCreateAth(params);

Judge check= newJudge(params);if(params.containsKey("-e")&&params.containsKey("-a")){

check.Judge();

tag= true;returntag;

}else if(params.containsKey("-n") || params.containsKey("-r") || params.containsKey("-d")) {

opera.createAth();

tag= true;returntag;

}returntag;

}private MapcheckParams(String[] args) {

Map params = new HashMap<>();for (int i = 0; i < args.length; i = i + 2) {

params.put(args[i], args[i+1]);

}returnparams;

}

}

EntryJudge

题目生成逻辑处理:

packageservice;importcom.sun.org.apache.xalan.internal.xsltc.compiler.util.StringStack;importpo.Deposit;importpo.Fraction;importutil.FileUtil;importpo.ChildDeposit;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;importjava.util.Stack;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.ThreadLocalRandom;importjava.util.concurrent.TimeUnit;/*** 生成题目类*/

public classCreateAth{private int maxNum = 100; //生成题目的整数最大值

private int denArea = 20; //分母的范围

private int maxCount = 10;//生成题目数量

privateDeposit content;private static final String[] SYMBOLS = newString[]{"+", "-", "x", "\u00F7"};/*** 生成随机题目,初始化,把主类中输入的参数内容调进来*/

public CreateAth(Mapparams) {for(String str : params.keySet()) {if (str.equals("-n")) {

maxCount=Integer.valueOf(params.get(str));

}else if (str.equals("-r")) {

maxNum=Integer.valueOf(params.get(str));

}else if (str.equals("-d")) {

denArea=Integer.valueOf(params.get(str));

}

}

}/*** 生成题目*/

private ExecutorService executor =Executors.newCachedThreadPool();public voidcreateAth() {

StringBuilder exercises= newStringBuilder();

StringBuilder answers= newStringBuilder();

List list = new ArrayList<>();long start =System.currentTimeMillis();for (int i = 1; i <=maxCount;) {

CreateAth generate= new CreateAth(true);if (!list.contains(generate)){

String[] strs= generate.print().split("=");

exercises.append(i).append(". ").append(strs[0]).append("\n");

answers.append(i).append(".").append(strs[1]).append("\n");

list.add(generate);

i++;

}

}

executor.execute(()-> FileUtil.writeFile(exercises.toString(), "Exercises.txt"));

executor.execute(()-> FileUtil.writeFile(answers.toString(), "Answers.txt"));

executor.shutdown();long end =System.currentTimeMillis();try{boolean loop = true;while(loop) {

loop= !executor.awaitTermination(30, TimeUnit.SECONDS); //超时等待阻塞,直到线程池里所有任务结束

} //等待所有任务完成

System.out.println("生成的" + maxCount + "道题和答案存放在当前目录下的Exercises.txt和Answers.txt,耗时为:"+(end - start) + "ms");

}catch(InterruptedException e) {

e.printStackTrace();

}

}

——————以下是生成题目所调用到的方法———————————/*** 生成组成题目随机数

* area:分母的范围*/

private int random(intarea) {

ThreadLocalRandom random=ThreadLocalRandom.current();int x =random.nextInt(area);if (x == 0) x = 1;returnx;

}/*** ThreadLocalRandom类在多线程环境中生成随机数。

* nextBoolean() 方法用于从随机数生成器的序列返回下一个伪随机的,均匀分布的布尔值*/

private booleanrandomBoolean() {

ThreadLocalRandom random=ThreadLocalRandom.current();returnrandom.nextBoolean();

}/*** 把生成的每个数进行处理得出分子分母*/

privateFraction creator() {if(randomBoolean()) {return new Fraction((random(maxNum)), 1);

}else{if(randomBoolean()) {int den =random(denArea);int mol = random(den *maxNum);return newFraction(den, mol);

}else{int den =random(denArea);return newFraction(random(den), den);

}

}

}/*** 单步计算

*@paramsymbol 符号

*@paramleft 左

*@paramright 右

*@return得出来结果后经过约分的分数*/

privateFraction calculate(String symbol, Fraction left, Fraction right) {switch(symbol) {case "+":returnleft.add(right);case "-":returnleft.subtract(right);case "x":returnleft.multiply(right);default:returnleft.divide(right);

}

}/*** 随机生成一道四则运算题目

*@paramfractionNum 运算符个数

*@return二叉树*/

private Deposit build(intfractionNum){if(fractionNum == 0){return new Deposit(creator(),null,null);

}

ThreadLocalRandom random=ThreadLocalRandom.current();

ChildDeposit node= new ChildDeposit(SYMBOLS [random.nextInt(4)],null, null);//左子树运算符数量

int left =random.nextInt(fractionNum);//右子树运算符数量

int right = fractionNum - left - 1;

node.setLeft(build(left));

node.setRight(build(right));

Fraction value=calculate(node.getSymbol(),node.getLeft().getValue(),node.getRight().getValue());//负数处理

if(value.Negative()){//交换左右子树,就是交换两个减数的顺序

if (node != null) {

Deposit swap=node.getLeft();

node.setLeft(node.getRight());

node.setRight(swap);

}

value=calculate(node.getSymbol(),node.getLeft().getValue(),node.getRight().getValue());

}

node.setValue(value);returnnode;

}/*** 获取表达式,

* 打印题目与答案*/

privateString print(){return print(content) + " = " +content.getValue();

}privateString print(Deposit node){if (node == null){return "";

}

String frac=node.toString();

String left=print(node.getLeft());if (node.getLeft() instanceof ChildDeposit && node instanceofChildDeposit) {if(bracketsLeft(((ChildDeposit) node.getLeft()).getSymbol(), ((ChildDeposit) node).getSymbol())) {

left= "(" + " " + left + " " + ")";

}

}

String right=print(node.getRight());if (node.getRight() instanceof ChildDeposit && node instanceofChildDeposit) {if(bracketsRight(((ChildDeposit) node.getRight()).getSymbol(), ((ChildDeposit) node).getSymbol())) {

right= "(" + " " + right + " " + ")";

}

}return left + frac +right;

}/*** 比较两个符号谁优先级更高,子树的箱号优先级低要加括号,左括号or右括号*/

private booleanbracketsLeft(String left,String mid){return (left.equals("+")|| left.equals("-")) && (mid.equals("x")||mid.equals("\u00F7"));

}private booleanbracketsRight(String right, String mid){return (right.equals("+")|| right.equals("-")) && (mid.equals("x")||mid.equals("\u00F7"))||(mid.equals("\u00F7"))||(mid.equals("-")&&(mid.equals("+")|| mid.equals("-")));

}/***生成一个题目,先调用下面的createAth方法来判断有没有生成一个用build方法生成的树*/CreateAth(booleanisBuild){if(isBuild){

ThreadLocalRandom random=ThreadLocalRandom.current();int kind = random.nextInt(4);if (kind == 0) kind = 1;

content=build(kind);while(content.getValue().Zero()){

content=build(kind);

}

}

}/*** 查重*/@Overridepublic booleanequals(Object o) {if (this == o) return true;if (!(o instanceof CreateAth)) return false;

CreateAth exercise=(CreateAth) o;returncontent.equals(exercise.content);

}

Fraction getResult() {returncontent.getValue();

}

——————以下是判断题目所要调用到的方法———————————/*** 中缀表达式生成树,用栈的特点,把中缀表达式变成前缀表达式

* 在判错中调用

*@paramexercise 中缀表达式

*@return二叉树*/Deposit build(String exercise) {

String[] strs= exercise.trim().split(" "); //拿走标号

Stack depositStack = new Stack<>(); //结点栈

StringStack symbolStack = new StringStack(); //符号栈//中缀表达式转换成前缀表达式,然后再用前序遍历生成数

for (int i = strs.length - 1; i >= 0; i--) {

String str=strs[i];if (!str.matches("[()+\\u00F7\\-x]")) {

depositStack.push(new Deposit(newFraction(str)));

}else{//符号结点

while (!symbolStack.empty() && ((symbolStack.peekString().equals("x") ||symbolStack.peekString().equals("\u00F7"))&& (str.equals("+") || str.equals("-"))|| str.equals("("))) {

String symbol=symbolStack.popString();if (symbol.equals(")")) {break;

}

push(symbol, depositStack);

}if (str.equals("(")) {continue;

}

symbolStack.pushString(str);

}

}while (!symbolStack.empty()) {

push(symbolStack.popString(), depositStack);

}this.content =depositStack.pop();returncontent;

}/*** 将符号压入节点栈且计算结果,仅在生成前缀表达式*/

private void push(String symbol, StacknodeStack) {

Deposit left=nodeStack.pop();

Deposit right=nodeStack.pop();

ChildDeposit node= newChildDeposit(symbol, left, right);

node.setValue(calculate(symbol, left.getValue(), right.getValue()));

nodeStack.push(node);

}

}

CreateAth

题目判错处理:判断答案的正确性,并记录下来正确题目与错误题目序号,打印到Grade.txt

packageservice;importpo.Fraction;importutil.FileUtil;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;/*** 答案判错类*/

public classJudge{private int trueNum; //正确数目

private int wrongNum; //错误数目

private String exerciseFileName; //题目文件名

private String answerFileName; //答案文件名

public Judge(Mapparams) {for(String str : params.keySet()) {if (str.equals("-e")) {

exerciseFileName=params.get(str);

}else if (str.equals("-a")) {

answerFileName=params.get(str);

}

}

}/*** 判断错误 ,并把错误写入文件*/

public voidJudge() {long start =System.currentTimeMillis();

List correctNums = new ArrayList<>();

List wrongNums = new ArrayList<>();

FileUtil.readFile((exercise, answer)->{

String[] strs1= exercise.split("\\."); //匹配每一行

String[] strs2 = answer.split("\\.");if (strs1[0].equals(strs2[0])) {

CreateAth exes= new CreateAth(false);

exes.build(strs1[1].trim()); //去掉两端的空格后,将后缀表达式生成树变成前缀的,

if (exes.getResult().equals(new Fraction(strs2[1].trim()))) { //答案两边都相等,继续执行下面的

correctNums.add(strs1[0]);

trueNum++;

}else{

wrongNums.add(strs1[0]);

wrongNum++;

}

}

}, exerciseFileName, answerFileName);

FileUtil.writeFile(printResult(correctNums, wrongNums),"Correction.txt");long end =System.currentTimeMillis();

System.out.println("题目答案对错统计存在当前目录下的Correction.txt文件下,耗时为:" + (end - start) + "ms");

}private String printResult(List correctNums, ListwrongNums) {

StringBuilder builder= newStringBuilder();

builder.append("Correct: ").append(trueNum).append(" (");for (int i = 0; i < correctNums.size(); i++) {if (i == correctNums.size() - 1) {

builder.append(correctNums.get(i));break;

}

builder.append(correctNums.get(i)).append(", ");

}

builder.append(")").append("\n");

builder.append("Wrong: ").append(wrongNum).append(" (");for (int i = 0; i < wrongNums.size(); i++) {if (i == wrongNums.size() - 1) {

builder.append(wrongNums.get(i));break;

}

builder.append(wrongNums.get(i)).append(", ");

}

builder.append(")").append("\n");returnbuilder.toString();

}

}

Judge

分数处理类:把所有随机生成的数都当成是分数处理,同时在些定义分数的四则运算方法

packagepo;/*** 1. 把所有随机生成的数都当成是分数处理(解决了自然整数,分数,带分数之间的差异)

* 2. 定义了分数的四则运算类*/

public classFraction{private int mol; //分子

private int den; //分母

/*** 处理随机生成的数值(约分等),组合分数对象*/

public Fraction(int mol, intden) {this.mol =mol;this.den =den;if (den <= 0) {throw new RuntimeException("分数分母不能为0");

}//否则就进行约分

int mod = 1;int max = den > mol ?den : mol;for (int i = 1; i <= max; i++) {if (mol % i == 0 && den % i == 0) {

mod=i;

}

}this.mol = mol /mod;this.den = den /mod;

}/*** 处理随机生成的数值,这个用于分解分数对象(仅在判错中使用)*/

publicFraction(String str) {int a = str.indexOf("'");int b = str.indexOf("/");if (a != -1) {//取出数组,转换类型

int c = Integer.valueOf(str.substring(0, a));

den= Integer.valueOf(str.substring(b + 1));

mol= c * den + Integer.valueOf(str.substring(a + 1, b));

}else if (b != -1) {

String[] sirs= str.split("/");

mol= Integer.valueOf(sirs[0]);

den= Integer.valueOf(sirs[1]);

}else{

mol=Integer.valueOf(str);

den= 1;

}

}/*** 定义加减乘除类,返回值类型(全都当成分数处理),由于要返回这个类的内容,所以方法前要加类名*/

publicFraction add(Fraction fraction) {return new Fraction(this.mol * fraction.den + this.den * fraction.mol, this.den *fraction.den);

}publicFraction subtract(Fraction fraction) {return new Fraction(this.mol * fraction.den - this.den * fraction.mol, this.den *fraction.den);

}publicFraction multiply(Fraction fraction) {return new Fraction(this.mol * fraction.mol, this.den *fraction.den);

}publicFraction divide(Fraction fraction) {return new Fraction(this.mol * fraction.den, this.den *fraction.mol);

}

}

Fraction

题目实现存放类以及其子类:

packagepo;importjava.util.Objects;/*** 用于存放题目的类,用二叉树的形式*/

public classDeposit{privateDeposit left;privateDeposit right;private Fraction value; //用于二叉树结点的是符号与运算结果数值的之间的变化

publicDeposit(Fraction value, Deposit left, Deposit right){this.value =value;this.left =left;this.right =right;

}/*** 取结点数据*/

publicFraction getValue() {returnvalue;

}publicDeposit getRight(){returnright;

}publicDeposit getLeft(){returnleft;

}/*** 设置结点数据*/

publicDeposit(Fraction value){this.value =value;

}public voidsetLeft(Deposit left){this.left =left;

}public voidsetRight(Deposit right){this.right =right ;

}public voidsetValue(Fraction value) {this.value =value;

}

@OverridepublicString toString() {returnvalue.toString();

}/*** 用于查重,判断二棵树是否相同*/@Overridepublic booleanequals(Object o) {if (this == o) return true;if (!(o instanceof Deposit)) return false;

Deposit node=(Deposit) o;return Objects.equals(value, node.value) &&Objects.equals(left, node.left)&&Objects.equals(right, node.right);

}

}

—————————————————————————————————————分割线——————————————————————————————————————————————packagepo;/*** 用于记录符号结点,与Deposit类是一样的道理*/

public class ChildDeposit extendsDeposit{privateString symbol;publicChildDeposit(String symbol, Deposit left, Deposit right){super(null, left, right);this.symbol =symbol;

}publicString getSymbol() {returnsymbol;

}

@OverridepublicString toString() {return " " + symbol + " ";

}/*** 用于查重*/@Overridepublic booleanequals(Object o) {if (this == o) return true;if (!(o instanceof ChildDeposit)) return false;

ChildDeposit that=(ChildDeposit) o;boolean flag = this.symbol != null &&symbol.equals(that.symbol);if(!flag) return false;boolean left = this.getLeft() != null &&getLeft().equals(that.getLeft());boolean right = this.getRight() != null &&getRight().equals(that.getRight());//左右子树相同

if(left &&right) {return true;

}if(left ^right) {return false;

}//如果是加法或乘法由于满足交换律所以要判断

if(this.symbol.equals("+") || this.symbol.equals("x")) {

left= this.getLeft() != null &&getLeft().equals(that.getRight());

right= this.getRight() != null &&getRight().equals(that.getLeft());

}return left &&right;

}

}

Deposit,ChildDeposit

文件读写工具类:处理文件的写入与读出

packageutil;import java.io.*;public classFileUtil{/*** 写入文件中*/

public static voidwriteFile(String content, String fileName) {

File file= newFile(fileName);try (BufferedWriter bw = new BufferedWriter(newFileWriter(file))){if(!file.exists()){

file.createNewFile();

}

bw.write(content);

bw.flush();

}catch(IOException e) {

System.out.println("文件操作失败...");

}

}/*** 读文件内容

*@paramcallBack 回调接口,分别处理每一行

*@paramexerciseFileName 题目文件

*@paramanswerFileName 答案文件*/

public static voidreadFile(ReaderCallBack callBack, String exerciseFileName, String answerFileName) {

File exerciseFile= newFile(exerciseFileName);

File answerFile= newFile(answerFileName);if(!exerciseFile.exists() || !answerFile.exists()) {

System.out.println("文件不存在!");return;

}try (BufferedReader br1 = new BufferedReader(newFileReader(exerciseFileName));

BufferedReader br2= new BufferedReader(newFileReader(answerFileName))) {

String line1, line2;while ((line1 = br1.readLine()) != null && (line2 = br2.readLine()) != null) {

callBack.deal(line1, line2);

}

}catch(IOException e) {

System.out.println("读取文件失败!");

}

}public interfaceReaderCallBack {void deal(String exercise, String answer) throwsIOException;

}

}

FileUtil

图形界面:

MainView :主页面,用户在两种操作中选一进行

packageview;import javax.swing.*;import java.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;public classMainView {public static voidmain(String[] args) {newMainJFrame();

}

}class MainJFrame extendsJFrame {/*** 程序主界面*/

private static final long serialVersionUID = 1;//定义全局变量

privateJLabel title;privateJButton generate,judge;privateJLabel result;private JPanel down = newJPanel();//创建一个容器

Container ct;

MainJFrame(){

ct=this.getContentPane();this.setLayout(null);//设置容器为空布局,绝对定位//标题

title= new JLabel("四则运算题目生成程序");

title.setFont(new Font("微软雅黑",Font.BOLD, 30));

title.setBounds(140, 40, 340, 100);//生成

generate = new JButton("生成题目");

generate.setBounds(120, 220, 140, 40);

generate.addActionListener(newgenerateListener());//判错

judge = new JButton("判断对错");

judge.setBounds(300, 220, 140, 40);

judge.addActionListener(newjudgeListenner());//添加组件

ct.add(title);

ct.add(generate);

ct.add(judge);

ct.add(down);this.setTitle("MyApp");this.setSize(600, 450);//设置窗口大小

this.setLocationRelativeTo(null);//基本设置 把窗口位置设置到屏幕中心

this.setVisible(true);//显示窗口

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //关闭窗口 当点击窗口的关闭按钮时退出程序(没有这一句,程序不会退出)

}class generateListener implementsActionListener {//监听生成按钮点击事件

public voidactionPerformed(ActionEvent e) {newGenerateView();

}

}class judgeListenner implementsActionListener{//监听判错按钮点击事件

public voidactionPerformed(ActionEvent e) {newJudgeView();

}

}

}

MainView

GenerateView:用户输入参数范围、生成题目的页面

packageview;importservice.EntryJudge;import javax.swing.*;import java.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;public class GenerateView extendsJFrame {privateJLabel title,result;privateJButton confirm;privateJLabel subjectNum,intArea,denArea;privateJTextField subjectNumField,intAreaField,denAreaField;private JPanel down = newJPanel();publicGenerateView() {//创建一个容器

Container ct;

ct=this.getContentPane();//this.setLayout(null);//设置容器为空布局,绝对定位

this.setSize(600, 450);//基本设置

this.setLocationRelativeTo(null);this.setLayout(null);

title= new JLabel("生成题目");

title.setFont(new Font("微软雅黑",Font.BOLD, 20));

title.setBounds(200,20,280,60);//生成题目数

subjectNum = new JLabel("请输入生成题目数:");

subjectNum.setFont(new Font("微软雅黑",Font.BOLD, 16));

subjectNum.setBounds(70,100,160,50);

subjectNumField= new JTextField (10);

subjectNumField.setBounds(260,100,260,40);//整数范围

intArea = new JLabel("请输入整数范围:");

intArea.setFont(new Font("微软雅黑",Font.BOLD, 16));

intArea.setBounds(70,150,160,50);

intAreaField= new JTextField (20);

intAreaField.setBounds(260,150,260,40);//分母范围

denArea = new JLabel("请输入分数分母的范围:");

denArea.setFont(new Font("微软雅黑",Font.BOLD, 16));

denArea.setBounds(70,200,180,50);

denAreaField= new JTextField (20);

denAreaField.setBounds(260,200,260,40);

confirm= new JButton("确定");

confirm.setBounds(250,270, 60, 50);

confirm.addActionListener(newConfirmActionListener());//设置底部panel

down.setBounds(130, 330, 280, 50);//设置底部panel背景透明

down.setBackground(null);

down.setOpaque(false);

result= newJLabel();

result.setFont(new Font("微软雅黑",Font.BOLD, 18));//添加组件

down.add(result);

ct.add(title);

ct.add(subjectNum);

ct.add(subjectNumField);

ct.add(intArea);

ct.add(intAreaField);

ct.add(denArea);

ct.add(denAreaField);

ct.add(confirm);

ct.add(down);this.setVisible(true);//显示窗口

this.setLocationRelativeTo(null);//基本设置 把窗口位置设置到屏幕中心

this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); //关闭窗口

}class ConfirmActionListener implementsActionListener {public voidactionPerformed(ActionEvent e) {

String args1=subjectNumField.getText();

String args2=intAreaField.getText();

String args3=denAreaField.getText();

String [] args= new String[]{"-n",args1,"-r",args2,"-d",args3};

EntryJudge ej= newEntryJudge();boolean res =ej.Entry(args);//获取命令执行结果

if(res == true) {

result.setText("结果:生成题目成功");

}else{

result.setText("结果:生成题目失败");

}

}

}

}

GenerateView

JudegeView:用户选择文件,是判断对错的页面

packageview;importservice.EntryJudge;import javax.swing.*;import java.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;public class JudgeView extendsJFrame {privateJLabel title,result;privateJButton confirm;privateJLabel subjectNum,intArea,file1,file2;privateJButton btn1,btn2;private JPanel down = newJPanel();publicJudgeView() {//创建一个容器

Container ct;

ct=this.getContentPane();this.setSize(600, 450);//基本设置

this.setLocationRelativeTo(null);this.setLayout(null);

title= new JLabel("判断对错");

title.setFont(new Font("微软雅黑",Font.BOLD, 20));

title.setBounds(200,20,280,60);//生成题目数

subjectNum = new JLabel("请选择题目文件:");

subjectNum.setFont(new Font("微软雅黑",Font.BOLD, 16));

subjectNum.setBounds(70,100,160,50);

btn1= new JButton ("选择文件");

btn1.setBounds(240,100,120,40);

btn1.addActionListener(newChooseFile1ActionListener());

file1= newJLabel();

file1.setFont(new Font("微软雅黑",Font.BOLD, 14));

file1.setBounds(390,100,130,50);//整数范围

intArea = new JLabel("请输入答案文件:");

intArea.setFont(new Font("微软雅黑",Font.BOLD, 16));

intArea.setBounds(70,150,160,50);

btn2= new JButton ("选择文件");

btn2.setBounds(240,150,120,40);

btn2.addActionListener(newChooseFile2ActionListener());

file2= newJLabel();

file2.setFont(new Font("微软雅黑",Font.BOLD, 14));

file2.setBounds(390,150,130,50);

confirm= new JButton("确定");

confirm.setBounds(250,270, 60, 50);

confirm.addActionListener(newConfirmActionListener());//设置底部panel

down.setBounds(130, 330, 280, 50);//设置底部panel背景透明

down.setBackground(null);

down.setOpaque(false);

result= newJLabel();

result.setFont(new Font("微软雅黑",Font.BOLD, 18));//添加组件

down.add(result);

ct.add(title);

ct.add(subjectNum);

ct.add(btn1);

ct.add(file1);

ct.add(intArea);

ct.add(btn2);

ct.add(file2);

ct.add(confirm);

ct.add(down);this.setVisible(true);//显示窗口

this.setLocationRelativeTo(null);//基本设置 把窗口位置设置到屏幕中心

this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); //关闭窗口

}class ChooseFile1ActionListener implementsActionListener {public voidactionPerformed(ActionEvent e) {

JFileChooser chooser= new JFileChooser(); //设置选择器

chooser.setMultiSelectionEnabled(true); //设为多选

int returnVal = chooser.showDialog(new JLabel(),"选择");

String filename= chooser.getSelectedFile().getName(); //获取绝对路径

file1.setText(filename);

}

}class ChooseFile2ActionListener implementsActionListener {public voidactionPerformed(ActionEvent e) {

JFileChooser chooser2= new JFileChooser(); //设置选择器

chooser2.setMultiSelectionEnabled(true); //设为多选

int returnVal = chooser2.showDialog(new JLabel(),"选择");

String filename= chooser2.getSelectedFile().getName(); //获取绝对路径

file2.setText(filename);

}

}class ConfirmActionListener implementsActionListener {public voidactionPerformed(ActionEvent e) {//获取结果

String [] args = new String[]{"-e", "Exercises.txt","-a","Answers.txt"};

EntryJudge ej= newEntryJudge();boolean res =ej.Entry(args);//获取命令执行结果

if(res == true) {

result.setText("结果:生成题目成功");

}else{

result.setText("结果:生成题目失败");

}

}

}

}

JudegeView

六、测试测试结果

程序运行,成功打开图形界面:

测试题目生成是否成功:

输入相关参数,点击确定 ,生成题目成功:

查看Exersises.txt,成功生成题目:

查看Answers.txt,答案:

测试判错程序:

改错二道题的答案:

打开判错的图形界面,选择算式文件、选择答案文件:

点击确定,查看统计文件Grade.txt:

生成一万道算式:

详细的github地址:

部分截图:

七、项目小结

此次的结对编程,我们均涉及了代码的设计、编写以及测试。讨论项目的基本构思对我负责的前期编码十分重要,从基础数值的定义和生成,再到采用二叉树存放算式,最后一步步完成项目,通过两个人的讨论,使得项目的整体思路变得清晰。在后期的编码中我的伙伴对我的编程方式提出了更有条理性的建议,最终在小改动下让整个项目的代码变得更为整洁与条理。

多讨论交流意见对编程的帮助很大,在判错功能中,从开始的没有头绪,到讨论出来通过应用一个栈的实例:中缀表达式转前缀表达式,解决了判错的问题;开始项目生成大数量题目的速度很慢,在小伙伴丰富的编程经验下,指出了关于多线程运行程序的方式,并且通过优化一些代码,大大提高了效率。

java编程实现素数环_结对编程(JAVA实现)相关推荐

  1. java编程实现素数环_回溯法解决素数环

    #include #include using namespace std;#define N 100000int isp[2 * N];int a[N];int visited[N];int is_ ...

  2. java编程实现素数环_素数环问题(Java)

    问题描述:将从1到n这n个整数围成一个圆环,若其中任意2个相邻的数字相加,结果均为素数,那么这个环就成为素数环. 分析:用回溯算法,考察所有可能的排列.然后通过剪枝(题目中的条件)筛选出可能的排列将其 ...

  3. 编程解决素数环问题Java_回溯法解决素数环问题java实现

    素数环问题: 输入正整数n,把整数1,2,3--,n组成一个环,使得相邻两个整数之和均为素数,输出所有方案,注意同一个环应恰好输出一次.n<==16 样例输入: 6 样例输出: 1 4 3 2  ...

  4. java小球碰撞实验报告_实验五 Java网络编程及安全 实验报告 20135232王玥

    北京电子科技学院(BESTI) 实验报告 课程:Java程序与设计         班级:1352 姓名:王玥 学号:20135232 成绩:             指导教师:娄嘉鹏       实 ...

  5. 结对编程_结对编程的利与弊

    结对编程 我在Menlo Innovations的夏季课程 杰玛·埃文斯 ( Gemma Evans)在Unsplash上拍摄的照片 许多程序员对结对编程持怀疑态度:敏捷软件开发技术,其中两个程序员在 ...

  6. JAVA实现数据库编程第三章_[数据库]使用Java实现数据库编程—03 第三章 高级查询(一)...

    [数据库]使用Java实现数据库编程-03 第三章 高级查询(一) 0 2018-07-21 03:01:10 1.修改表:(1)修改表名语法: ALTER TABLE RENAME [ TO ] : ...

  7. 结对编程_结对编程:使其工作的好处,技巧和建议

    结对编程 Pair Programming - a pair that's greater than the sum of its parts. You may have heard about pa ...

  8. 扇贝编程可以用c语言吗,扇贝编程手机版下载_扇贝编程app下载 v1.1.41 - 87G手游网...

    扇贝编程app是一款实用的编程学习软件,为不同基础的用户制定了不同阶段的学习方案,从入门到应用一应俱全,包括基础课.网络爬虫.数据分析等内容,就算是新手小白也能在扇贝编程很快学会.扇贝编程app拥有丰 ...

  9. 核桃编程学python吗_学编程要趁早?对话核桃编程X支付宝 “小程序编程马拉松”三强得主...

    原标题:学编程要趁早?对话核桃编程X支付宝 "小程序编程马拉松"三强得主 来源:网络 浏览专业IT技术社区网站,创作沉浸式互动体验游戏,开发辟谣小程序......这并不是程序员大神 ...

最新文章

  1. 一文拆解中国火星车着陆全过程
  2. Linux安装GitLib
  3. HttpClientFactory 结合 Polly 轻松实现重试机制
  4. Python2.7-sqlite3
  5. 【maven】mvn deploy 报错 Failed to deploy artifacts: Could not transfer artifact
  6. 探寻京东云核心竞争力的源泉
  7. 基于40万表格数据集TableBank,用MaskRCNN做表格检测
  8. java连接数据库增删改查公共方法
  9. 信号的时频分析MATLAB,基于matlab的信号时频分析仿真
  10. 微信公众号生成个性化推广二维码
  11. 【渝粤题库】陕西师范大学100021 大学语文 作业 (专升本、高起专、高起本)
  12. Typora 实用教程
  13. cdr拼版工具不见了_cdr排版及各种问题解决方法
  14. 天下大事,必做于细!
  15. 如何免费下载qq音乐 ----ctf学习之php漏洞
  16. mysql进阶教程pdf_Mysql基础到进阶精品视频教程附讲义文档 91课
  17. php imap 库_php imap_open 实例教程
  18. 论文阅读 Hourglass:Stacked Hourglass Networks for Human Pose Estimation
  19. Power BI项目之某公司内部数据分析
  20. 服务器刷怪塔制作方法,教程/刷怪塔 - Minecraft Wiki,最详细的官方我的世界百科...

热门文章

  1. 直接点oracle表编辑器,DbForge Studio for Oracle入门教程:如何在表编辑器中创建表...
  2. CSDN之人人code,整数取反
  3. 设计模式--观察者模式
  4. Android实现监测网络状态
  5. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 19丨行转列【难度中等】​
  6. 刚入职场的菜鸟,这些大数据知识点,你必须掌握了!
  7. Linux 中 VIM 的使用
  8. 怎么批量改计算机名字,怎么给照片批量重命名
  9. STM32与宇电设备实现AI-BUS通讯
  10. __declspec(naked)和__asm编写实践总结