结队编程--java实现
1、GitHub地址:https://github.com/caiyouling/Myapp
队友:钟小敏 GitHub地址:https://github.com/zhongxiao136/Myapp
2.PSP表格
| PSP | Personal Software Process Stages | 预计耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 40 | 30 |
| .Estimate | .估计这个任务需要多少时间 | 40 | 30 |
| Development | 开发 | 1320 | 1330 |
| .Analysis | .需求分析 | 100 | 120 |
| .Design Spec | .生成设计文档 | 40 | 30 |
| .Design Review | .设计复审 | 15 | 20 |
| .Coding Standard | .代码规范 | 15 | 20 |
| .Design | .具体设计 | 90 | 100 |
| .Coding | .具体编码 | 850 | 950 |
| .Code Review | .代码复审 | 90 | 90 |
| .Test | .测试(自我测试,修改代码,提交修改) | 120 | 130 |
| Reporting | 报告 | 180 | 160 |
| .Test Report | .测试报告 | 90 | 80 |
| .Size Measurement | .计算工作量 | 30 | 40 |
| .Postmortem&Process Improvement Plan | .事后总结,并提出过程改进计划 | 60 | 40 |
| 合计 | 1540 | 1520 |
效能分析


设计实现过程
- 解题思路:由于题目为自动生成小学四则运算题目,所以需要系统随机生成运算式。运算式包含操作数和运算符,所以我们就用了一个CreateNumAndOpe类随机生成,然后利用createExpression类组合成运算式和答案(利用Calculate类进行计算,因为结果要为真分数或整数,所以我们利用Fraction类对分数进行操作化为最终形式),然后用createFile类生成题目,答案和做题文件,最后,利用Grade类进行验证答案,并生成成绩文件。
- createNumAndOpe类:定义运算符数组;包含createNumber()和createOperator(),其中createNumber()用于生成操作数(利用Random类随机生成整数或分数);createOperator()用于生成运算符,随机从运算符数组中选出运算符。
- createExpression类:用于生成随机带括号的运算式,其中exerciseAndAnswer()根据操作数数组和运算符数组生成运算式,其中需要用Random类判断是否随机生成括号,最后用Calculate()计算出结果。getExerciseAndAnswer()根据用户输入的题目个数和数字范围生成相应的题目和答案。
- Calculate类:根据运算式转为字符数组,然后逐个遍历,建立一个运算符栈和操作数栈,利用中缀表达式转为后缀表达式进行计算。
- 因为括号的优先级最高,所以我们先判断是否为'(',若是则入运算符栈;
- 判断是否为')',是则从操作数取出两个进行运算,将'('之后的运算符都拿出来算,结果再存入操作数栈中,然后左括号出栈;
- 判断是否为运算符,当运算符优先级小于运算符栈顶元素,则取两个操作数进行运算,直至栈顶优先级不高于该运算符,结果存入操作数栈中,然后该运算符入栈;
- 若为操作数,因为生成的操作数有分数,但是我们转为了字符数组,就需要根据'/'作为标志获取分子和分母,若为整数,我们将分母设为1,把操作数都转为分数进行传到calculate()进行运算,返回结果
- Fraction类:对分数进行操作,其中getFinalFraction()用于生成最终分数形式,gcd()利用辗转相除法生成最大公约数,getRealFraction()将假分数转为真分数。
- createFile类:利用System.getProperTy()获取当前路径生成printFile文件夹,利用BufferedWriter写入题目文件,答案文件和答题文件(用户进行答题的文件),存放到printFile文件夹下
- Grade类:用于验证用户答题是否正确,利用BufferedReader读取Writer文件和answer文件进行比较,算出对错的题数及题号写入Grade文件,也存在printFile文件夹下。
- 各类之间的调用如下:

- Frame类:图形化界面,用户点击‘生成文件’,即会生成练习文件,在左面板也会出现题目,中间面板进行答题,点击查看答案,则会有正确答案在右侧面板,查看成绩后会弹出成绩框。
关键代码说明
- 用于生成操作数和运算符的方法:1.利用随机生成的flag=[0,1],当flag为0时,生成整数 2.当flag为1时,生成分子和分母组成分数
public static String[] createNumber(int number, int round){
Random random = new Random();
String[] num = new String[number];
for (int i = 0; i < number; i++) {
//用于判断生成整数还是分数
int flag = (int)(Math.random()*10) % 2;
if(flag == 0){ //生成整数
int n = random.nextInt(round);
if(n == 0){
num[i] = 1 + "";
}else{
num[i] = n +"";
}
}else{ //生成分数
//随机生成分子和分母
int numerator = random.nextInt(round);
int denominator = random.nextInt(round);;
while(numerator>=denominator || numerator==0 || denominator==0){ //判断是否为真分数,且不能生成带0的分数
numerator = random.nextInt(round);
denominator = random.nextInt(round);
}
//拼装成分数形式
num[i] = numerator + "/" + denominator;
}
}
return num;
}
/**
* 随机生成运算符
* 将四则运算符放入一个静态不可变的operatorTypes[]字符数组中
* 随机产生index到数组中取操作符
*/
private static final Character[] operatorTypes = {'+' , '-' , '×' , '÷'};
public static Character[] createOperators(int number) {
Character[] operators = new Character[number];
for (int i = 0; i < number; i++) {
//随机获取运算符的类型(0~3 代表4个运算符(+、-、×、÷)的类型)
int index = (int)(Math.random()*4);
Character operatorType = operatorTypes[index];
operators[i] = operatorType;
}
return operators;
}
public static int priority(Character character) {
switch(character) {
case '×':
case '÷':return 1;
case '+':
case '-':return 0;
}
return -1;
}
private static String[] exerciseAndAnswer(String[] numbers, Character[] operators){
Random random = new Random();
//获得操作数的数量
int num = numbers.length;
//随机生成带括号的算式
int isAddBracket = (int)(Math.random()*10) %2;
if(isAddBracket == 1){ //当isAddBracket==1时,生成带括号的表达式
//当标记为1时代表该操作数已经添加了左括号
int[] leftBracket = new int[num];
//当标记为1时代表该操作数已经添加了右括号
int[] rightBracket = new int[num];
//遍历操作数数组,随机添加括号
for (int index = 0 ; index<num-1 ; index++) {
int n = (int)(Math.random()*10)%2;
if(n == 0 && rightBracket[index] != 1) {//判断当前操作数是否标记了左括号
leftBracket[index] = 1; //标记已生成左括号
numbers[index] = "(" + numbers[index]; //操作数之前加上左括号
int k = num - 1;
//生成右括号的位置(左括号的位置~最后)
int rightBracketIndex = random.nextInt(k)%(k-index) + (index+1);
//如果当前操作数有左括号,则重新生成括号位置
while (leftBracket[rightBracketIndex] == 1){
rightBracketIndex = random.nextInt(k)%(k-index) + (index+1);
}
rightBracket[rightBracketIndex] = 1; //标记已生成右括号
numbers[rightBracketIndex] = numbers[rightBracketIndex] +")";
}
}
}
//将运算符数组和操作数数组交替拼成一个算式字符串
StringBuilder str = new StringBuilder(numbers[0]);
for (int k = 0; k < operators.length; k++) { //组成算式
str.append(operators[k]).append(numbers[k + 1]);
}
//将算式转换为String
String exercise = str.toString();
//获取运算式结果
String answer = Calculate.getAnswer(exercise);
if(answer.equals("-")){ //运算过程出现负数则返回null
return null;
}
return new String[]{exercise,answer};
}
public static String getAnswer(String expression) {
Stack<Character> operators = new Stack<Character>(); //运算符栈,用于存放运算符
Stack<Fraction> fractions = new Stack<Fraction>(); //操作数栈,用于存放操作数
if(expression == null){
return "null";
}
char[] chars = expression.toCharArray(); //将表达式字符串转成字符数组
//遍历获取处理
for (int i = 0 ; i<chars.length ; i++) {
char c = chars[i];
if(c == '('){ //先处理有括号的情况,如果是左括号,入栈
operators.push(c);
} else if(c == ')'){ //当前字符为右括号
while(operators.peek() != '('){ //当运算符栈顶的元素不为‘(’,则继续
Fraction fraction1 = fractions.pop(); //拿取操作栈中的两个分数
Fraction fraction2 = fractions.pop();
//获取计算后的值
Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
fraction2.getNumerator(), fraction2.getDenominator());
if(result.getNumerator()<0){ //判断是否为负数
return "-";
}
//将结果压入栈中
fractions.push(result);
}
//将左括号出栈
operators.pop();
}else if(isOperator(c)){//是运算符
//当运算符栈不为空,且当前运算符优先级小于栈顶运算符优先级
while(!operators.empty() && !(priority(c)>= priority(operators.peek()))){
//拿取操作栈中的两个分数
Fraction fraction1 = fractions.pop();
Fraction fraction2 = fractions.pop();
//获取计算后的值
Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
fraction2.getNumerator(), fraction2.getDenominator());
if(result.getNumerator()<0){
return "-";
}
//将结果压入栈中
fractions.push(result);
}
//将运算符入栈
operators.push(c);
}else{ //是操作数
if(c >= '0' && c <= '9'){
StringBuilder fra = new StringBuilder();
//对分式进行处理
while(i<chars.length && (chars[i]=='/' || ((chars[i]>='0') && chars[i]<='9'))){
fra.append(chars[i]);
i++;
}
i--;
//到此 fra里面是一个操作数
String val = fra.toString();
//标记‘/’的位置
int flag = val.length();
for(int j = 0 ; j<val.length() ; j++){
if(val.charAt(j)=='/'){//当获取的数值存在/则标记/的位置,便于接下来划分分子和分母生成分数对象
flag = j;
}
}
//把操作数拆成分式计算
//分子
StringBuilder numeratorBuf = new StringBuilder();
//分母
StringBuilder denominatorBuf = new StringBuilder();
for(int k = 0 ; k<flag; k++){
numeratorBuf.append(val.charAt(k));
}
//判断是否为分数
if(flag != val.length()){
for(int m = flag+1 ; m<val.length() ; m++){
denominatorBuf.append(val.charAt(m));
}
}else{//如果不是分数则分母计为1
denominatorBuf.append('1');
}
//将分子分母分别入栈
fractions.push(new Fraction(Integer.parseInt(numeratorBuf.toString()),
Integer.parseInt(denominatorBuf.toString())));
}
}
}
while(!operators.empty() && fractions.size()>1){
Fraction fraction1 = fractions.pop();
Fraction fraction2 = fractions.pop();
//获取计算后的值
Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
fraction2.getNumerator(), fraction2.getDenominator());
if(result.getNumerator()<0){
return "-";
}
//将结果压入栈中
fractions.push(result);
}
//计算结果
Fraction result = fractions.pop();
//获取最终的结果(将分数进行约分)
return Fraction.getFinalFraction(result);
}
//用于验证答案是否正确,correct的题数用字符串拼接,再分割字符串
public static void isAnswer(String writeFilePath, String answerFilePath) {
//获取用户答题和练习答案的文件
File writeFile = new File(writeFilePath);
File answerFile = new File(answerFilePath);
//找到存放Writer.txt和Answer.txt文件的文件夹
String fileDirectory = System.getProperty("user.dir") + File.separator + "PrintFile";
File gradeFile = new File(fileDirectory, "Grade.txt");//在当前文件夹下生成Grade.txt文件
if(writeFile.isFile() && answerFile.isFile()) {
try {
BufferedReader writeReader = new BufferedReader(new InputStreamReader(new FileInputStream(writeFile)));
BufferedReader answerReader = new BufferedReader(new InputStreamReader(new FileInputStream(answerFile)));
//储存错题和对题
String correct = "";
String wrong = "";
int correctNum = 0;
int wrongNum = 0;
//记录错题对题的题号
int index = 1;
String write = null;
String answer = null;
System.out.println("开始验证答案···");
//通过一行行读取文件比较答题情况
while((( write= writeReader.readLine()) != null) && ((answer = answerReader.readLine()) != null)){
if(write.equals(answer)){
//将答对的题用字符串拼接
correct += "," + index;
index ++;
correctNum ++;
}else{
wrong += "," + index;
index ++;
wrongNum ++;
}
}
//用于生成Grade.txt文件内容
if(correctNum > 0){
correct = "Correct: " + correctNum + "(" + correct.substring(1) +")" + "\r\n" ;
}else{
correct = "Correct: 0" +"\r\n";
}
if(wrongNum > 0){
wrong = "Wrong: " + wrongNum + "(" + wrong.substring(1) +")";
}else{
wrong = "Wrong: 0";
}
//写入文件
BufferedWriter gradeFileBuf = new BufferedWriter(new FileWriter(gradeFile));
gradeFileBuf.write(correct);//将correct 和wrong写入文件
gradeFileBuf.write(wrong);
System.out.print(correct);//控制台也打印答题情况
System.out.println(wrong);
if(writeReader != null){
writeReader.close();
}
if(answerReader != null){
answerReader.close();
}
if(gradeFileBuf != null){
gradeFileBuf.close();
}
public static String getFinalFraction(Fraction fraction) {
int denominator = fraction.getDenominator(); //获得分子分母
int numerator = fraction.getNumerator();
if(numerator == 0){ //若分子为0,则输出0
return "0";
}
if(denominator == 0){
return "";
}
int a = gcd(numerator, denominator); //c是分子分母的最大公因数,将分子分母化简
if (denominator == 1 ) { //分母为1
return numerator + "";
}
if(numerator == denominator){
return 1+"";
}else {
if(numerator > denominator){ //分子大于分母化为真分数
fraction = getRealFraction(fraction); //假分数分割为商和余数,余数作为新的分子
if(fraction.getNumerator() == 0){//若余数为0,则代表整除
return fraction.getInter()+"";
}else {
return fraction.getInter() + "'" + fraction.getNumerator()/a + "/" + fraction.getDenominator()/a;
}
}else{ //其他情况化简分数
return numerator/a + "/" + denominator/a;
}
}
}
private static int gcd(int numerator, int denominator){
if(numerator%denominator == 0){
return denominator; //获取最大公因数
}else{
return gcd(denominator,numerator%denominator); //辗转相除法递归获取最大公因数
}
}
测试
1.启动main,运行控制台打印

2.用户输入后生成的文件列表
.
3.生成题目文件的题目

4.生成答案放入文件

5.在答题文件进行答题后

6.重新判别答案

7.传入参数不正确的情况

8.输入不正确的文件名

9.生成一万道题目

10.生成一万道题目的答案文件

11.初始界面

12.点击生成文件

13.输入生成个数

14.生成题目

15.进行作答

16.输出成绩

17.生成十道题

18.生成一万道

小结
- 本次结对项目中,我们都受益匪浅,结对项目需要多次讨论得出一致结论,两个人一起分析问题解决bug是比一个人好一些,虽然也有发生歧义的地方,但是不同角度看问题,才能实现1+1>2,这次编程作业让我们对java有了更深了了解,但也还有很多不足的
结队编程--java实现的更多相关文章
- 20175126Apollo 20175126《Java程序设计》结队编程项目——四则运算
结队编程项目——四则运算 一.项目需求 自动生成小学四则运算题目(加.减.乘.除)统计正确率 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 需求分析: 生成四则运算:需要使用 ...
- 结队编程study
##今天针对study进行结队编程,我和搭档张佳慧刚开始误认为SystemBarTintManager,baseActivity是personalActivity跳转的下一个界面,因为这个代码个人中心 ...
- 结队编程第二次作业:Android自动生成算式应用
一.题目要求 本次作业要求两个人合作完成,驾驶员和导航员角色自定,鼓励大家在工作期间角色随时互换,这里会布置两个题目,请各组成员根据自己的爱好任选一题. 这次我和我的小伙伴选择了题目一. 题目1: 实 ...
- Atitit.异步编程 java .net php python js 对照
Atitit.异步编程 java .net php python js 的比較 1. 1.异步任务,异步模式, APM模式,, EAP模式, TAP 1 1.1. APM模式: Beg ...
- Atitit.异步编程 java .net php python js 的比较
Atitit.异步编程 java .net php python js 的比较 1. 1.异步任务,异步模式, APM模式,, EAP模式, TAP 1 1.1. APM模式: Beg ...
- Java并发编程-Java内存模型
JVM内存结构与Java内存模型经常会混淆在一起,本文将对Java内存模型进行详细说明,并解释Java内存模型在线程通信方面起到的作用. 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存 ...
- UDP协议网络Socket编程(java实现C/S通信案例)
我的博客园:https://www.cnblogs.com/chenzhenhong/p/13825286.html 我的CSDN博客:https://blog.csdn.net/Charzous/a ...
- socket编程-java
一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...
- 基础编程-java之股神
买了一支股票,他知道从他买股票的那天开始,股票会有以下变化:第一天不变,以后涨一天,跌一天,涨两天,跌一天,涨三天,跌一天...依此类推. 为方便计算,假设每次涨和跌皆为1,股票初始单价也为1,请计算 ...
随机推荐
- H3C交换机如何删除VLAN
H3C交换机如何删除VLAN,如果直接使用“undo vlan”是删不干净的,因为配置VLAN时还配置过接口. 1.首先通过console口或telnet连接三层交换机,进入用户视图模式”<H3 ...
- HDFS修改副本数,并生效。
1.hadoop集群使用的ucloud的uahdoop 2.是公司集群配置小,只有两台core节点,实际就是两台的datanode. 容量占用超过了80%,需要缩减副本以空出容量. 3.查看 hado ...
- vue---将json导出Excel的方法
在做数据表格渲染的时候,经常遇到的需求的就是将导出excel,下面是使用vue进行项目数据进行导出的方法. 一.安装依赖 npm i -S file-saver npm i -S xlsx 二.在sr ...
- Java数组移位和统计
package com.imooc.method; import java.util.InputMismatchException; import java.util.Scanner; public ...
- Hive学习笔记——metadata
Hive结构体系 https://blog.csdn.net/zhoudaxia/article/details/8855937 可以在hive的jdbc接口中使用getMetaData方法来获取hi ...
- light4j一个轻量级的低延时、高吞吐量、内存占用量小的API平台
1.背景(abstract) 笔者算是一个极客类型的程序员了.喜欢探索一些程序内在的原理.稳定性.自动化运维.健壮性,很多时间也会 去对程序的内存使用率.cpu使用率锱铢必较.尽量克扣掉不必要的cpu ...
- Python - Django - 中间件 process_template_response
process_template_response(self, request, response) 有两个参数,response 是 TemplateResponse 对象(由视图函数或者中间件产生 ...
- 【论文阅读】3DMM-A Morphable Model for The Synthesis of 3D Faces
前言 参考 1. 2. 完
- 头部文件jq嵌入笔记
var headHtml = '<div class="container">' + '<div class="navbar-header"& ...
- nginx配置优化提高并发量
1 nginx配置优化提高并发量 worker_processes 2; 这个按照CPU的核数来决定 2 worker_connections 65535; 这个一般设置65535即可 每个进程允许的 ...