结对项目
这个作业属于哪个课程 | 22级计科2班 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业目标 | 实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。 |
Github地址:https://github.com/JiangJiazhe/PairWork/tree/main
项目设计
函数功能
-
main模块:设置main函数通过命令行设置参数,函数接受到参数后首先判断参数个数是否符合要求;然后根据"-n","-r","-e","-a"四个标志来获取需要的参数,再判断参数有无结对传入以及传入的参数是否满足构成调用generateQuestions或check()函数的要求,若满足则调用不满足要求则报错;
-
GenerateQuestion模块:设置按数量与数值范围的要求生成题目集的函数generateQuestions(),定义Question类集合获取生成的题目集,利用循环调用generateExpression()函数来生成表达式,并调用evaluate()函数解析表达式生成答案,最后将序号,表达式、答案包装成Question类对象添加进集合里,从而得到问题集,并调用;
-
GenerateExpression模块:主要函数为生成表达式generateExpression()函数,该函数调用generateSimpleExpression()
生成简单表达式,调用generateComplexExpression()生成复杂表达式,且设置随机调用;而这两个函数又会调用生成整数generateOperand()函数与生成真分数generateTrueFraction()函数来随机生成整数和真分数以构造出随机算术表达式,最终通过generateExpression()函数返回算术表达式的字符串形式; -
ExpressionParsing模块:主要函数为evaluate()函数,通过设置操作数栈和操作符栈,对传进来的表达式进行一个个的读取入栈;当字符为数字类型时入操作数栈,当字符为操作符类型时(通过isOperator()函数判断)入操作符栈;随后通过判断操作符优先级(通过hasPrecedence()函数以及有无括号"()"来判断)计算出答案,该过程调用applyOp()函数实现,且整个计算过程是一边入栈一边计算的,当满足计算要求时计算结果,然后将结果入栈,后续继续计算直到结束;最后返回的是一个真分数类变量,该变量作为答案后续可进行自动字符串转换;
-
CorrectExpression模块:设置check()函数获取两个文件路径参数,通过BufferedReader类读取文本内容,在函数中利用循环的单行读取来获取两个文件相同序号题目的答案字符串,然后判断两个字符串大小是否相同,相同则代表答案一样,对correct字符串变量进行修改,不相同则对wrong字符串变量进行修改,最终得到两个存放正确编号和错误编号的字符串变量,并将其打印进指定文件;
-
Save模块:内含saveQuestionsToFile()函数通过循环来打印问题到问题文件中去,saveAnswerToFile()函数通过循环打印答案到答案文件;
部分代码展示
package main;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class CorrectExpression {
public CorrectExpression() {
}
public static void check(String checkFile, String answersFile) throws IOException {
BufferedReader readerCheck = new BufferedReader(new InputStreamReader(new FileInputStream(checkFile)));
BufferedReader answerCheck = new BufferedReader(new InputStreamReader(new FileInputStream(answersFile)));
String checkLine = "";
String answerLine = "";
String correct = "";
String wrong = "";
int correctNum = 0;
int wrongNum = 0;
while((checkLine = readerCheck.readLine()) != null && (answerLine = answerCheck.readLine()) != null) {
String[] checkString = checkLine.split("\\.");
String[] answerString = answerLine.split("\\.");
if (checkString[1].equals(answerString[1])) {
if (correct.equals("")) {
correct = answerString[0];
++correctNum;
} else {
correct = correct + "," + answerString[0];
++correctNum;
}
} else if (wrong.equals("")) {
wrong = answerString[0];
++wrongNum;
} else {
wrong = wrong + "," + answerString[0];
++wrongNum;
}
}
readerCheck.close();
answerCheck.close();
OutputStreamWriter resultWriter = new OutputStreamWriter(new FileOutputStream(new File("D:\\Grade.txt")), "UTF-8");
resultWriter.append("Correct:" + correctNum + "(" + correct + ")");
resultWriter.append("\n");
resultWriter.append("Wrong:" + wrongNum + "(" + wrong + ")");
resultWriter.close();
System.out.println("Correct:" + correctNum + "(" + correct + ")");
System.out.println("Wrong:" + wrongNum + "(" + wrong + ")");
}
}
package main;
import java.util.Stack;
public class ExpressionParsing {
public ExpressionParsing() {
}
public static Fraction evaluate(String expression) {
Stack<Character> operators = new Stack();
Stack<Fraction> value = new Stack();
for(int i = 0; i < expression.length(); ++i) {
char c = expression.charAt(i);
if (Character.isDigit(c)) {
int number;
for(number = 0; i < expression.length() && Character.isDigit(expression.charAt(i)); ++i) {
number = number * 10 + (expression.charAt(i) - 48);
}
int denominator = 1;
int numerator = number;
if (i < expression.length() && expression.charAt(i) == '\'') {
++i;
numerator = expression.charAt(i++) - 48;
++i;
denominator = expression.charAt(i++) - 48;
numerator += number * denominator;
} else if (i < expression.length() && expression.charAt(i) == '/') {
++i;
denominator = expression.charAt(i++) - 48;
}
value.push(new Fraction(numerator, denominator));
--i;
} else if (c != ' ' && c != '=') {
if (c == '(') {
operators.push(c);
} else if (c == ')') {
while(!operators.isEmpty() && (Character)operators.peek() != '(') {
value.push(applyOp((Character)operators.pop(), (Fraction)value.pop(), (Fraction)value.pop()));
}
if (!operators.isEmpty()) {
operators.pop();
}
} else if (isOperator(c)) {
while(!operators.isEmpty() && (Character)operators.peek() != '(' && hasPrecedence(c, (Character)operators.peek())) {
value.push(applyOp((Character)operators.pop(), (Fraction)value.pop(), (Fraction)value.pop()));
}
operators.push(c);
}
}
}
while(!operators.isEmpty()) {
value.push(applyOp((Character)operators.pop(), (Fraction)value.pop(), (Fraction)value.pop()));
}
return (Fraction)value.pop();
}
private static boolean isOperator(char c) {
return c == '+' || c == '-' || c == 215 || c == 247;
}
private static Fraction applyOp(char op, Fraction f1, Fraction f2) {
switch(op) {
case '+':
return f1.add(f2);
case '-':
return f2.subtract(f1);
case '×':
return f1.multiply(f2);
case '÷':
return f2.divide(f1);
default:
return new Fraction(1, 1);
}
}
private static boolean hasPrecedence(char op1, char op2) {
return op1 != 215 && op1 != 247 || op2 != '+' && op2 != '-';
}
}
package main;
public class Fraction {
public int numerator;
public int denominator;
public Fraction(int numerator, int denominator) {
if (denominator == 0) {
throw new IllegalArgumentException("Denominator cannot be zero");
} else {
this.numerator = numerator;
this.denominator = denominator;
this.reduce();
}
}
private void reduce() {
int gcd = this.gcd(this.numerator, this.denominator);
this.numerator /= gcd;
this.denominator /= gcd;
}
private int gcd(int a, int b) {
return b == 0 ? a : this.gcd(b, a % b);
}
public Fraction add(Fraction other) {
int newNumerator = this.numerator * other.denominator + this.denominator * other.numerator;
int newDenominator = this.denominator * other.denominator;
return new Fraction(newNumerator, newDenominator);
}
public Fraction subtract(Fraction other) {
int newNumerator = this.numerator * other.denominator - this.denominator * other.numerator;
int newDenominator = this.denominator * other.denominator;
return new Fraction(newNumerator, newDenominator);
}
public Fraction multiply(Fraction other) {
int newNumerator = this.numerator * other.numerator;
int newDenominator = this.denominator * other.denominator;
return new Fraction(newNumerator, newDenominator);
}
public Fraction divide(Fraction other) {
if (other.numerator == 0) {
throw new IllegalArgumentException("Cannot divide by zero");
} else {
int newNumerator = this.numerator * other.denominator;
int newDenominator = this.denominator * other.numerator;
return new Fraction(newNumerator, newDenominator);
}
}
public String toString() {
if (this.numerator == 0) {
return this.numerator.makeConcatWithConstants<invokedynamic>(this.numerator);
} else if (Math.abs(this.numerator) >= Math.abs(this.denominator)) {
if (this.denominator == 1) {
return this.numerator.makeConcatWithConstants<invokedynamic>(this.numerator);
} else if (this.denominator == -1) {
return (-this.numerator).makeConcatWithConstants<invokedynamic>(-this.numerator);
} else {
int whole = this.numerator / this.denominator;
return whole + "'" + (this.numerator - whole * this.denominator) + "/" + this.denominator;
}
} else {
return this.numerator + "/" + this.denominator;
}
}
}
package main;
import java.util.Random;
public class GenerateExpression {
private static final String[] OPERATORS = new String[]{"+", "-", "×", "÷"};
private static final int MIN_OPERAND = 1;
private static final int MIN_EXPRESSION_PARTS = 2;
private static final int MAX_RECURSION_DEPTH = 3;
private static final Random random = new Random();
public GenerateExpression() {
}
public static String generateExpression(int minParts, int maxParts, int MaxNumber) {
if (minParts <= maxParts && minParts >= 2) {
int parts = minParts + random.nextInt(maxParts - minParts + 1);
return parts < 3 ? generateSimpleExpression(parts, MaxNumber) : generateComplexExpression(parts, MaxNumber);
} else {
throw new IllegalArgumentException("Invalid range for expression parts");
}
}
private static String generateSimpleExpression(int parts, int MaxNumber) {
StringBuilder sb = new StringBuilder();
if (parts == 1) {
if (random.nextBoolean()) {
sb.append(generateTrueFraction(MaxNumber));
} else {
sb.append(generateOperand(MaxNumber));
}
sb.append(' ');
return sb.toString();
} else {
int flat = 0;
for(int i = 0; i < parts - 1; ++i) {
if (random.nextInt(5) == 1) {
sb.append(generateTrueFraction(MaxNumber));
} else {
sb.append(generateOperand(MaxNumber));
}
sb.append(' ');
String Op = OPERATORS[random.nextInt(OPERATORS.length)];
if (Op == "-") {
++flat;
}
sb.append(Op);
sb.append(' ');
}
if (random.nextInt(5) == 1) {
sb.append(generateTrueFraction(MaxNumber));
} else {
sb.append(generateOperand(MaxNumber));
}
return sb.toString();
}
}
private static String generateComplexExpression(int parts, int MaxNumber) {
if (random.nextBoolean()) {
return generateSimpleExpression(parts, MaxNumber);
} else {
int subParts = random.nextInt(parts - 1) + 1;
String left = generateSimpleExpression(subParts, MaxNumber);
String right = generateSimpleExpression(parts - subParts, MaxNumber);
if (subParts != 1 && parts - subParts != 1) {
return "(" + left + ")" + OPERATORS[random.nextInt(OPERATORS.length)] + "(" + right + ")";
} else {
return subParts == 1 ? left + OPERATORS[random.nextInt(OPERATORS.length)] + "(" + right + ")" : "(" + left + ")" + OPERATORS[random.nextInt(OPERATORS.length)] + right;
}
}
}
private static int generateOperand(int MAX_OPERAND) {
return 1 + random.nextInt(MAX_OPERAND - 1 + 1);
}
public static String generateTrueFraction(int MAX_OPERAND) {
int denominator = random.nextInt(9) + 1;
int numerator;
for(numerator = random.nextInt(9) + 1; numerator >= denominator; denominator = random.nextInt(9) + 1) {
numerator = random.nextInt(9) + 1;
}
int wholePart = random.nextInt(MAX_OPERAND);
return wholePart == 0 ? numerator + "/" + denominator : wholePart + "'" + numerator + "/" + denominator;
}
}
package main;
import java.util.Random;
public class GenerateExpression {
private static final String[] OPERATORS = new String[]{"+", "-", "×", "÷"};
private static final int MIN_OPERAND = 1;
private static final int MIN_EXPRESSION_PARTS = 2;
private static final int MAX_RECURSION_DEPTH = 3;
private static final Random random = new Random();
public GenerateExpression() {
}
public static String generateExpression(int minParts, int maxParts, int MaxNumber) {
if (minParts <= maxParts && minParts >= 2) {
int parts = minParts + random.nextInt(maxParts - minParts + 1);
return parts < 3 ? generateSimpleExpression(parts, MaxNumber) : generateComplexExpression(parts, MaxNumber);
} else {
throw new IllegalArgumentException("Invalid range for expression parts");
}
}
private static String generateSimpleExpression(int parts, int MaxNumber) {
StringBuilder sb = new StringBuilder();
if (parts == 1) {
if (random.nextBoolean()) {
sb.append(generateTrueFraction(MaxNumber));
} else {
sb.append(generateOperand(MaxNumber));
}
sb.append(' ');
return sb.toString();
} else {
int flat = 0;
for(int i = 0; i < parts - 1; ++i) {
if (random.nextInt(5) == 1) {
sb.append(generateTrueFraction(MaxNumber));
} else {
sb.append(generateOperand(MaxNumber));
}
sb.append(' ');
String Op = OPERATORS[random.nextInt(OPERATORS.length)];
if (Op == "-") {
++flat;
}
sb.append(Op);
sb.append(' ');
}
if (random.nextInt(5) == 1) {
sb.append(generateTrueFraction(MaxNumber));
} else {
sb.append(generateOperand(MaxNumber));
}
return sb.toString();
}
}
private static String generateComplexExpression(int parts, int MaxNumber) {
if (random.nextBoolean()) {
return generateSimpleExpression(parts, MaxNumber);
} else {
int subParts = random.nextInt(parts - 1) + 1;
String left = generateSimpleExpression(subParts, MaxNumber);
String right = generateSimpleExpression(parts - subParts, MaxNumber);
if (subParts != 1 && parts - subParts != 1) {
return "(" + left + ")" + OPERATORS[random.nextInt(OPERATORS.length)] + "(" + right + ")";
} else {
return subParts == 1 ? left + OPERATORS[random.nextInt(OPERATORS.length)] + "(" + right + ")" : "(" + left + ")" + OPERATORS[random.nextInt(OPERATORS.length)] + right;
}
}
}
private static int generateOperand(int MAX_OPERAND) {
return 1 + random.nextInt(MAX_OPERAND - 1 + 1);
}
public static String generateTrueFraction(int MAX_OPERAND) {
int denominator = random.nextInt(9) + 1;
int numerator;
for(numerator = random.nextInt(9) + 1; numerator >= denominator; denominator = random.nextInt(9) + 1) {
numerator = random.nextInt(9) + 1;
}
int wholePart = random.nextInt(MAX_OPERAND);
return wholePart == 0 ? numerator + "/" + denominator : wholePart + "'" + numerator + "/" + denominator;
}
}
public static void main(String[] args) throws IOException {
int numberOfQuestions = -1;
int maxNumber = -1;
String filename1 = "";
String filename2 = "";
if (args.length < 2) {
System.err.println("输入参数至少两个");
} else {
String[] var5 = args;
int QuestionNumber = args.length;
for(int var7 = 0; var7 < QuestionNumber; ++var7) {
String arg = var5[var7];
if (arg.startsWith("-n")) {
try {
numberOfQuestions = Integer.parseInt(arg.substring(2));
} catch (NumberFormatException var13) {
System.err.println("Invalid argument after -n: " + arg);
System.exit(1);
}
System.out.println(numberOfQuestions);
}
if (arg.startsWith("-r")) {
try {
maxNumber = Integer.parseInt(arg.substring(2));
} catch (NumberFormatException var12) {
System.err.println("Invalid argument after -r: " + arg);
System.exit(1);
}
System.out.println(maxNumber);
}
if (arg.startsWith("-e")) {
try {
filename1 = arg.substring(2);
} catch (NumberFormatException var11) {
System.err.println("Invalid argument after -e: " + arg);
System.exit(1);
}
System.out.println(filename1);
}
if (arg.startsWith("-a")) {
try {
filename2 = arg.substring(2);
} catch (NumberFormatException var10) {
System.err.println("Invalid argument after -a: " + arg);
System.exit(1);
}
System.out.println(filename2);
}
}
if (numberOfQuestions != -1 && maxNumber != -1 || !filename1.equals("") && !filename2.equals("")) {
if (numberOfQuestions != -1 && maxNumber != -1) {
GenerateQuestion.generateQuestions(numberOfQuestions, maxNumber);
}
if (!filename1.equals("") && !filename2.equals("")) {
BufferedReader exercise = new BufferedReader(new InputStreamReader(new FileInputStream(filename1)));
QuestionNumber = 0;
String Expression = "";
ArrayList questions = new ArrayList();
while((Expression = exercise.readLine()) != null) {
++QuestionNumber;
String answer = ExpressionParsing.evaluate(Expression).toString();
questions.add(new Question(QuestionNumber, Expression, answer));
}
Save.saveAnswerToFile(questions, "D:\\SyAnswer.txt");
CorrectExpression.check(filename2, "D:\\SyAnswer.txt");
}
} else {
System.err.println("参数输入方式不对");
}
}
}
}
结果展示
PSP分析
PSP2.1 | Person Software Process Stages | 预估耗时(min) | 实际耗时(min) |
---|---|---|---|
Planning | 计划 | 60 | 60 |
.Estimate | 预估这个任务需要多少时间 | 60 | 120 |
.Development | 开发 | 600 | 1040 |
· Analysis | 需求分析(包括学习新技术 | 60 | 90 |
· Design Spec | 生成设计文档 | 30 | 30 |
· Design Review | 设计复审 | 20 | 15 |
· Coding Standard | 代码规范(为目前的开发制定合适的规范) | 30 | 20 |
· Design | 具体设计 | 30 | 30 |
· Coding | 具体编码 | 120 | 240 |
· Code Review | 代码复审 | 30 | 40 |
· Test | 测试(自我测试、修改代码、提交修改) | 30 | 40 |
Reporting | 报告 | 30 | 40 |
· Test Report | 测试报告 | 20 | 20 |
· Size Measurement | 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 30 | 30 |
Total | 合计 | 1180 | 1755 |
总结
由于java是初学,而且这次作业是独立完成的,所以有不少功能未能实现,希望能够吸取这次的教训,下次将作业完成的更好。