软件工程第二次结对作业
1.题目要求.
我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子,请实现它,要求:
(1)能够自动生成四则运算练习题
(2)可以定制题目数量
(3)用户可以选择运算符
(4)用户设置最大数(如十以内、百以内等)
(5)用户选择是否有括号、是否有小数
(6) 用户选择输出方式(如输出到文件、打印机等)
(7)最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)
2.任务分配.
此次结对作业的任务分配为:
驾驶员:王新哲
能够完成全部代码工作,程序基本实现全部要求功能,并将代码上传至coding.net或者GitHub代码托管系统中。
能够对导航员在本次编程工作中起到的作用给出客观评价,并完成500字以上的总结,并且上传和队友工作时的照片。
领航员:刘春慧
能够辅助驾驶员完成全部代码工作,并且为关键函数选用合适的覆盖标准设计测试用例,并编写代码进行单元自动测试。
能够根据上次作业的代码检查表对驾驶员的本次工作进行评价。
能够对本次工作完成500字以上的总结。
3.代码部分.
源代码地址
代码分为两个主要功能:一个为自动生成四则运算题目,另一个为做完题目后检查正确率。
主函数:
package caculate;
import caculate.JudgeAnswerController;
import caculate.ProducerController;
import java.util.Scanner;
public class Caculate{
public static void main(String[] args){
System.out.println("请选择功能:");
System.out.println(" 1. 四则运算生成器");
System.out.println(" 2. 答案对比");
System.out.print("请输入你的选择[1/2]:");
int choose = new Scanner(System.in).nextInt();
switch (choose){
case 1:
ProducerController producerController = new ProducerController();
producerController.ConstructProblem();break;
case 2:
JudgeAnswerController judgeAnswerController = new JudgeAnswerController();
judgeAnswerController.start();break;
default:
System.out.println("输入不正确,请输入1或2");main(args);break;
}
}
}
整数生成器与真分数生成器函数
public String[] createProblem(int range){
Random random = new Random();
int operatorCount = 1 + random.nextInt(3); //随机操作符的个数(1-3个)
int operand[] = new int[operatorCount + 1]; //操作数个数
int[] operatorIndex = index(operatorCount, 4, random);
for(int i = 0; i < operatorCount + 1; i++){
operand[i] = random.nextInt(range);
}
String formula = stitchingFormula(operatorCount, operand, operatorIndex);
//计算结果
Calculator calculator = new Calculator();
int res = calculator.algorithm(formula);
String formulaRes[] = new String[2];
if (res > 0){
formulaRes[0] = formula;
formulaRes[1] = String.valueOf(res);
}else {
return createProblem(range);
}
return formulaRes;
}
public String[] createProblem(int range){
Random random = new Random();
int operatorCount = 1 + random.nextInt(3); //操作符的个数1-3
CreateInteger create = new CreateInteger();
int[] operatorIndex = create.index(operatorCount,2, random); //操作符的下标
//生成第一个操作数
int[] coprimeNumber1 = createCoprimeNumbers(range, random);
int x = coprimeNumber1[0];
int y = coprimeNumber1[1];
String s = shamToProperFraction(x, y);
for(int i=0; i < operatorCount; i++){
//生成剩下的操作数
int[] coprimeNumber = createCoprimeNumbers(range, random);
int numx = coprimeNumber[0];
int numy = coprimeNumber[1];
String currentOpreator = OPERATOR[operatorIndex[i]];
if(currentOpreator.equals("+")){ //加法
x = x * numy + y * numx;
y = y * numy;
}else { //减法
int count = 0;
while(x * numy - y * numx < 0){ //差为负数
coprimeNumber = createCoprimeNumbers(range, random);
numx = coprimeNumber[0];
numy = coprimeNumber[1];
count++;
if (count >= 5){
numx = x - 1;
numy = y;
}
}
x = x * numy - y * numx;
y = y * numy;
}
String num = shamToProperFraction(numx, numy);
s += currentOpreator + num;
}
int greatFactor = greatFactor(x,y);
x /= greatFactor; //最终结果化简
y /= greatFactor;
String res = shamToProperFraction(x, y);
s += "=";
String formulaRes[] = {s, res};
return formulaRes;
}
/**
* 假分数转化为真分数
* @param x 分子
* @param y 分母
* @return
*/
public String shamToProperFraction(int x, int y){
if (x > y){
int n = x / y;
x = (x - n * y);
if (x == 0){
return String.valueOf(n);
}
return n + "'" + x + "/" + y;
}else if (x == y){
return "1";
}else if (y == 1){
return String.valueOf(x);
}else if (x == 0){
return "0";
}
return x + "/" + y;
}
}
计算结果函数
public int algorithm(String s) {
Stack<Integer> numStack = new Stack<>(); //放数字
Stack<String> operatorStack = new Stack<>(); //放操作符
HashMap<String, Integer> hashMap = new HashMap<>(); //存放运算符优先级
hashMap.put("(", 0);
hashMap.put("+", 1);
hashMap.put("-", 1);
hashMap.put("*", 2);
hashMap.put("÷", 2);
String formula = s.replaceAll(" ", "");
for (int i = 0; i < formula.length();) {
StringBuilder digit = new StringBuilder(); //StringBuilder类中的方法主要偏重于对于字符串的变化,例如追加、插入和删除等,这个也是StringBuffer和String类的主要区别。
char c = formula.charAt(i); //将式子字符串切割为c字符
while (Character.isDigit(c)) { //判断字符是否为10进制数字,将一个数加入digit
digit.append(c);
i++;
if (i < formula.length()){
c = formula.charAt(i);
}else {
break;
}
}
if (digit.length() == 0){ //当前digit里面已经无数字,即当前处理符号
switch (c) {
case '(': {
operatorStack.push(String.valueOf(c));//如果是( 转化为字符串压入字符栈
break;
}
case ')': { //遇到右括号了计算,因为(的优先级最高
String stmp = operatorStack.pop(); //如果是),将符号栈栈顶元素取到
while (!operatorStack.isEmpty() && !stmp.equals("(")) { //当前符号栈里面还有+ - * /
int a = numStack.pop(); //取操作数a,b
int b = numStack.pop();
int result = calculate(b, a, stmp); //计算
if(result < 0)
return -1;
numStack.push(result); //将结果压入栈
stmp = operatorStack.pop(); //符号指向下一个计算符号
}
break;
}
case '=': { //遇到等号了计算
String stmp;
while (!operatorStack.isEmpty()) { //当前符号栈里面还有+ - * /,即还没有算完
stmp = operatorStack.pop();
int a = numStack.pop();
int b = numStack.pop();
int result = calculate(b, a, stmp);
if(result < 0)
return -1;
numStack.push(result);
}
break;
}
default: { //不满足之前的任何情况
String stmp;
while (!operatorStack.isEmpty()) { //如果符号栈有符号
stmp = operatorStack.pop(); //当前符号栈,栈顶元素
if (hashMap.get(stmp) >= hashMap.get(String.valueOf(c))) { //比较优先级
int a = numStack.pop();
int b = numStack.pop();
int result =calculate (b, a, stmp);
if(result < 0)
return -1;
numStack.push(result);
}
else {
operatorStack.push(stmp);
break;
}
}
operatorStack.push(String.valueOf(c)); //将符号压入符号栈
break;
}
}
}
else { //处理数字,直接压栈
numStack.push(Integer.valueOf(digit.toString())); //Integer.valueof()返回的是Integer对象,而Integer.parseInt()返回的是int型
continue; //结束本次循环,回到for语句进行下一次循环,即不执行i++(因为此时i已经指向符号了)
}
i++;
}
return numStack.peek(); //返回栈底数字即等式的答案。
}
判断结果函数
public void start(){
System.out.print("请输入待验证答案路径:");
Scanner scanner = new Scanner(System.in);
String exerciseFilePath = scanner.next();
System.out.print("请输入程序生成答案文件路径:");
String answerFilePath = scanner.next();
try {
List<String> exerciseAnswers = exerciseFileReader(exerciseFilePath);
List<String> answers = answerReader(answerFilePath);
List<String> correct = new ArrayList<>();
List<String> wrong = new ArrayList<>();
int max = Math.max(exerciseAnswers.size(), answers.size());
int num = 1;
for (int i = 0; i < max; i++){
if (exerciseAnswers.get(i).equals(answers.get(i))){
correct.add(String.valueOf(num++));
}else {
wrong.add(String.valueOf(num++));
}
}
File grade = new File("Grade.txt");
if (grade.exists()){
grade.delete();
}
if (grade.createNewFile()){
FileOutputStream gradeOutput = new FileOutputStream(grade);
PrintStream gradePrintStream = new PrintStream(gradeOutput);
String corrects = String.join(",", correct);
gradePrintStream.println("Correct:" + correct.size() +
" (" + corrects + ")");
String wrongs = String.join(",", wrong);
gradePrintStream.println("Wrong:" + wrong.size() +
" (" + wrongs + ")");
}
System.out.println("判定完成");
} catch (FileNotFoundException e) {
System.out.println("文件不存在");
} catch (IOException e) {
System.out.println("文件读入异常");
}
}
public List<String> exerciseFileReader(String path) throws IOException {
BufferedReader exerciseReader = new BufferedReader(new FileReader(path));
String exerciseAnswer = "";
List<String> exerciseAnswers = new ArrayList<>();
while ((exerciseAnswer = exerciseReader.readLine()) != null){
String[] split = exerciseAnswer.split("=");
if (split[1] != null){
exerciseAnswers.add(split[1]);
}else {
exerciseAnswers.add(" ");
}
}
return exerciseAnswers;
}
生成四则运算截图:
正确答案截图:
做题截图:
答案对比截图:
4.总结.
此次编程中,我和我的领航员-刘春慧同学一起完成了代码的初步内容,在我编程过程中给了我很大帮助,给我提了很多宝贵意见,在我遇到困难和bug的时候,总是给我很大帮助。但由于我五一放假回家,导致我们编程的时间较少,因此代码也有很多不足之处:没有图形化界面,部分代码不够简洁。我们也在网上找了很多资料,翻了很多关于java的书希望解决我们的不足,但终究是临时抱佛脚,没什么用,希望将来的我在再看见这段代码时,有能力将它完善地更好。
通过此次的编程作业,我对java有了更进一步的认识和掌握,与同伴配合地更默契,但因为对java掌握的不够牢靠,学习java的时间较短,编程时经常出现一些错误,思路有时也会有些混乱,但这也体现了结对编程的好处,领航员可以在旁边提醒我的错误,帮助我改正错误。
现在的我对于java的大部分知识还是无法很好的掌握和运用,面对此次作业还是比较吃力的,这也提醒我要继续学习,加强动手能力和实践能力,毕竟实践出真知嘛,不能好逸恶劳,觉得自己什么都会了,因为自己的一点点成绩而沾沾自喜。学习java的路还很漫长,需要自己一步一个脚印的继续学习,不能一蹴而就。