结对对对项目
这个作业属于哪个课程 | 软件工程课程 |
---|---|
这个作业要求在哪里 | 个人项目 - 作业 - 计科22级34班 - 班级博客 - 博客园 (cnblogs.com) |
这个作业的目标 | 按照要求写一个四则运算生成器 |
成员一 | 3122004883许億驰 |
任务列表
[x] 1. 实现命令行程序:
- 创建一个命令行程序,能够生成小学四则运算题目。
[x] 2. 支持参数控制:
- 使用
-n
参数控制生成题目的个数。 - 使用
-r
参数控制题目中数值的范围。 - 该参数必须给定,否则程序报错并给出帮助信息。
[x] 3. 确保计算过程无负数:
- 生成的题目中计算过程不能产生负数。
- 如果存在形如
e1 - e2
的子表达式,那么e1 ≥ e2
。
[x] 4. 确保除法结果为真分数:
- 生成的题目中如果存在形如
e1 ÷ e2
的子表达式,那么其结果应是真分数。
[x] 5. 限制运算符个数:
- 每道题目中出现的运算符个数不超过3个。
[x] 6. 确保题目不重复:
- 程序一次运行生成的题目不能重复。
- 任何两道题目不能通过有限次交换
+
和×
左右的算术表达式变换为同一道题目。
[x] 7. 生成题目文件:
- 生成的题目存入执行程序的当前目录下的
Exercises.txt
文件。 - 真分数在输入输出时采用特定格式(如
3/5
和2’3/8
)。
[x] 8. 生成答案文件:
- 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的
Answers.txt
文件。 - 真分数的运算结果应符合规范。
[x] 9. 支持题目数量:
- 程序应能支持一万道题目的生成。
[x] 10. 支持答案校验:
- 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计。
- 输入参数为
-e <exercisefile>.txt -a <answerfile>.txt
。 - 统计结果输出到文件
Grade.txt
,格式如下:Correct: 5 (1, 3, 5, 7, 9) Wrong: 5 (2, 4, 6, 8, 10)
作业正文
效能分析
性能分析中,代码执行时间最长的是
function generateQuestions() {
const numQuestions = parseInt(document.getElementById('numQuestions').value, 10);
questions = [];
answers = [];
const questionsDiv = document.getElementById('questions');
questionsDiv.innerHTML = '';
for (let i = 0; i < numQuestions; i++) {
let question, answer;
// 随机选择是否包含括号
const hasBrackets = Math.random() > 0.5;
if (hasBrackets) {
question = generateExpressionWithBrackets();
} else {
question = generateSimpleExpression();
}
questions.push(question);
// 计算答案
answer = calculateAnswer(question);
answers.push(answer);
const questionElement = document.createElement('p');
questionElement.innerText = `${i + 1}. ${question} =`;
questionsDiv.appendChild(questionElement);
}
}
设计思路
设计思路是通过一个前端网页来生成、下载、上传和校验小学四则运算题目。以下是对你设计思路的详细解释:
-
用户界面:
创建一个包含输入框和按钮的网页界面,用户可以通过输入框设置生成题目的数量和数值范围。
提供文件上传功能,用户可以上传包含答案和题目的文本文件。
提供按钮用于生成题目、下载题目和答案、以及计算正确率。 -
生成题目:
用户输入生成题目的数量和数值范围。
程序根据用户输入生成指定数量的四则运算题目,并显示在页面上。
生成的题目可能包含括号和带分数。 -
下载题目和答案:
用户可以点击按钮下载生成的题目和答案,分别保存为 questions.txt 和 answers.txt 文件。
上传答案和题目文件:
用户可以上传包含答案和题目的文本文件。
程序解析上传的文件,提取题目和答案,并显示在页面上。
计算正确率:
用户上传答案文件后,程序可以计算上传答案的正确率。
程序将正确率和正确/错误的题目编号输出到 grade.txt 文件中。 -
具体实现步骤
生成题目:
使用 generateQuestions 函数生成题目,根据用户输入的数量和数值范围生成题目。
使用 generateSimpleExpression 和 generateExpressionWithBrackets 函数生成简单的和带括号的表达式。
使用 generateNumber 函数生成自然数或带分数。
计算答案:
使用 calculateAnswer 函数计算表达式的答案。
使用 convertToMathExpression 函数将带分数转换为浮点数以便计算。
下载题目和答案:
使用 downloadQuestions 和 downloadAnswers 函数将生成的题目和答案下载为文本文件。
上传答案和题目文件:
使用 parseAnswersFile 和 parseQuestionsFile 函数解析上传的答案和题目文件。
提取文件内容并显示在页面上。
计算正确率:
使用 calculateAccuracy 函数计算上传答案的正确率。
将正确率和正确/错误的题目编号输出到 grade.txt 文件中。
代码结构
HTML部分:
包含输入框和按钮,用于设置生成题目的数量和数值范围,以及上传和下载文件。
包含一个显示题目的容器。
JavaScript部分:
包含生成题目、计算答案、下载文件、上传文件和计算正确率的函数。
使用 document.getElementById 获取页面元素,使用 FileReader 读取上传的文件。
代码说明
let questions = [];
let answers = [];
let userAnswers = [];
function generateQuestions() {
const numQuestions = parseInt(document.getElementById('numQuestions').value, 10);
questions = [];
answers = [];
const questionsDiv = document.getElementById('questions');
questionsDiv.innerHTML = '';
for (let i = 0; i < numQuestions; i++) {
let question, answer;
// 随机选择是否包含括号
const hasBrackets = Math.random() > 0.5;
if (hasBrackets) {
question = generateExpressionWithBrackets();
} else {
question = generateSimpleExpression();
}
questions.push(question);
// 计算答案
answer = calculateAnswer(question);
answers.push(answer);
const questionElement = document.createElement('p');
questionElement.innerText = `${i + 1}. ${question} =`;
questionsDiv.appendChild(questionElement);
}
}
function generateSimpleExpression() {
const num1 = generateNumber();
const num2 = generateNumber();
const operator = ['+', '-', '*', '/'][Math.floor(Math.random() * 4)];
return `${num1} ${operator} ${num2}`;
}
function generateExpressionWithBrackets() {
const num1 = generateNumber();
const num2 = generateNumber();
const num3 = generateNumber();
const operator1 = ['+', '-', '*', '/'][Math.floor(Math.random() * 4)];
const operator2 = ['+', '-', '*', '/'][Math.floor(Math.random() * 4)];
return `(${num1} ${operator1} ${num2}) ${operator2} ${num3}`;
}
function generateNumber() {
const numSetUp = parseInt(document.getElementById('numSetUp').value, 10);
const isFraction = Math.random() > 0.5;
if (isFraction) {
const numerator = Math.floor(Math.random() * 10) + 1;
const denominator = Math.floor(Math.random() * 10) + 1;
return convertToMixedNumber(`${numerator}/${denominator}`);
} else {
return Math.floor(Math.random() * numSetUp).toString();
}
}
function convertToMixedNumber(fraction) {
const [numerator, denominator] = fraction.split('/').map(Number);
const integerPart = Math.floor(numerator / denominator);
const remainder = numerator % denominator;
if (integerPart === 0) {
return fraction;
}else if (remainder ===0){
return integerPart;
} else {
return `${integerPart}'${remainder}/${denominator}`;
}
}
function calculateAnswer(expression) {
// 使用 eval 计算表达式,注意 eval 的安全性问题
try {
return eval(convertToMathExpression(expression));
} catch (e) {
return 'Error';
}
}
function convertToMathExpression(expression) {
// 将带分数转换为浮点数
return expression.replace(/(\d+)'(\d+)\/(\d+)/g, (match, p1, p2, p3) => parseFloat(p1) + parseFloat(p2) / parseFloat(p3));
}
function downloadQuestions() {
if (questions.length === 0) {
alert('请先生成题目!');
return;
}
const questionsText = questions.map((q, i) => `${i + 1}. ${q}`).join('\n');
const allText = `题目:\n${questionsText}\n`;
const blob = new Blob([allText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'questions.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
function downloadAnswers() {
if (answers.length === 0) {
alert('请先生成题目!');
return;
}
const answersText = answers.map((a, i) => `${i + 1}. ${a}`).join('\n');
const allText = `答案:\n${answersText}\n`;
const blob = new Blob([allText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'answers.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
function parseAnswersFile(file) {
userAnswers = [];
const reader = new FileReader();
reader.onload = function(event) {
const content = event.target.result;
const lines = content.split('\n').map(line => line.trim()).filter(line => line);
const answersText = lines.join('\n');
userAnswers = answersText.replace(/^答案:\n/, '').split('\n').map(line => line.trim().split('. ')[1]).filter(line => line);
};
reader.readAsText(file);
}
function parseQuestionsFile(file) {
questions = [];
const reader = new FileReader();
reader.onload = function(event) {
const content = event.target.result;
const lines = content.split('\n').map(line => line.trim()).filter(line => line);
const answersText = lines.join('\n');
questions = answersText.replace(/^题目:\n/, '').split('\n').map(line => line.trim().split('. ')[1]).filter(line => line);
const questionsDiv = document.getElementById('questions');
questionsDiv.innerHTML = '';
// 显示题目
questions.forEach((question) => {
const questionElement = document.createElement('p');
questionElement.innerText = `${question}=`;
questionsDiv.appendChild(questionElement);
});
// 计算答案
questions.forEach((question) => {
const answer = calculateAnswer(question);
answers.push(answer);
});
};
reader.readAsText(file);
}
function calculateAccuracy(userAnswers) {
let rightGrade = [];
let wrongGrade = [];
if (userAnswers.length === 0) {
alert('请先上传答案文件!');
return;
}
if (answers.length === 0) {
alert('请先生成题目!');
return;
}
let correctCount = 0;
for (let i = 0; i < answers.length; i++) {
if (parseFloat(userAnswers[i]) === answers[i]) {
rightGrade.push(i + 1);
correctCount++;
}else {
wrongGrade.push(i + 1);
}
}
const accuracy = (correctCount / answers.length) * 100;
alert(`正确率: ${accuracy.toFixed(2)}%`);
grade = `正确题目:${rightGrade.join(', ')}\n错误题目:${wrongGrade.join(', ')}\n正确率: ${accuracy.toFixed(2)}%\n`;
const blob = new Blob([grade], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'grade.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
测试运行
运行效果
PSP表格如下
2.在PSP表格[附录2]记录下你估计在程序开发各个步骤上耗费的时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 70 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 35 |
Development | 开发 | 240 | 260 |
· Analysis | · 需求分析(包括学习新技术) | 45 | 50 |
· Design Spec | · 生成设计文档 | 40 | 45 |
· Design Review | · 设计复审 | 30 | 35 |
· Coding Standard | · 代码规范(为目前的开发制定合适的规范) | 20 | 25 |
· Design | · 具体设计 | 50 | 55 |
· Coding | · 具体编码 | 100 | 110 |
· Code Review | · 代码复审 | 25 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 30 | 40 |
Reporting | 报告 | 60 | 65 |
· Test Report | · 测试报告 | 20 | 25 |
· Size Measurement | · 计算工作量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 20 | 30 |
合计 | 360 | 400 |
项目小结
-
在本次结对编程项目中,我们共同开发了一个小学四则运算生成器。该程序能够生成指定数量和数值范围的四则运算题目,支持带分数和括号的表达式,并能够下载题目和答案文件,上传答案文件进行校验,计算正确率。
-
结对编程能够提高代码质量,减少错误。两个人可以互相监督,及时发现和解决问题。
-
结对编程能够提高开发效率,两个人可以分工合作,共同完成任务。