!!发布啦!!!小学生用过都说好之网页版四则运算器!
github地址
https://github.com/18yy/mycounter
团队介绍
- 队名:731的三号铺和四号铺
- 队员:王馨玮,杨艺
题目要求:实现一个自动生成小学四则运算题目的命令行程序。
- 使用 -n 参数控制生成题目的个数。
- 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围。
- 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
- 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
- 每道题目中出现的运算符个数不超过3个。
- 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。
- 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件。
- 程序应能支持一万道题目的生成。
- 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计。
设计思路和过程
- 最开始:一开始开始写的时候只写了一个运算符的情况,用switch语句匹配,这个情况比较简单,运算也写好了,但是后来想扩展到多个运算符,发现在原来的基础上修改效果不理想且代码十分冗长复用性不高,于是重新换个思路
- 换个思路: 由 randomQus函数随机生成问题的集合,由countIt函数计算问题的结果,(具体思路接下会讲到),大概功能实现后,就开始写具体的分数整数运算和下载文件及页面样式和把题目展现在页面及计算对错等功能。
关键代码及说明
- 做准备的函数
- 随机函数getRandomNum:利用js的random函数来获得范围内的随机数。
//随机函数,获得min到max范围内的随机数包括min和max
function getRandomNum(min, max) {
var range = max - min
return Math.round(Math.random() * range + min)
}
- 求最大公约数函数getGCD:辗转相除法求得。
//求最大公约数
function getGCD(a, b) {
var temp
while (b != 0) {
temp = a % b
a = b
b = temp
}
return a
}
- 把分数转化为真分数或整数函数TrueFract:如果分子能整除分母,则化为整数,如果不能整除,求最大公约数,最大公约数为1时则比较分子分母大小,分子比分母大则化为带分数形式反之分子分母约去公约数并返回分数形式。
//把分数转化为真分数或整数
function TrueFract(molecule, denominator) {
if (molecule % denominator != 0) {
var gcd = getGCD(molecule, denominator)
if (gcd == 1) {
if (molecule > denominator) {
//带分数
//最大公约数为1且分子大于分母,用真分数形式表示
var n = parseInt(molecule / denominator)
molecule = molecule % denominator
var sum = n + "'" + molecule + '/' + denominator
} else {
//分子小于分母的分数
var sum = molecule + '/' + denominator
}
} else {
//化简
molecule = molecule / gcd
denominator = denominator / gcd
var sum = TrueFract(molecule, denominator)
}
} else {
//可整除,结果为整数
var sum = parseInt(molecule / denominator)
}
return sum
}
- 获取真分数中的分子函数getMolecule和获取真分数中分母函数getDenominator:判断分数中是否有'/'和'''来区分整数和分子小于分母的分数,当分数为带分数时利用split函数获得整数部分,分子部分,分母部分,返回分母和用分子和整数相乘后的分子。
//获取真分数中的分子
function getMolecule(n) {
var str = String(n)
if (str.search('/') == -1) {
//整数
var molecule = n
} else if (str.search("'") == -1) {
//分子小于分母的分数
var molecule = str.split('/')[0]
} else {
//带分数
var integer = parseInt(str.split("'")[0])
var fraction = str.split("'")[1]
var denominator = parseInt(fraction.split('/')[1])
var molecule = parseInt(fraction.split('/')[0])
var molecule = integer * denominator + molecule
}
return parseInt(molecule)
}
//获取真分数中的分母
function getDenominator(n) {
var str = String(n)
if (str.search('/') == -1) {
//整数
var denominator = 1
} else {
//分数
var denominator = parseInt(str.split('/')[1])
}
return denominator
}
- 进行一个运算符计算的函数playCount:传入的参数为运算的两个数和运算符号,由于题目有可能是分数或整数,使用进行运算的两个数都当成分数来运算,再用swicth匹配运算符进行运算,接下来只要需要运算的时候,调用即可。
//进行一个运算符的运算
function playCount(a, b, operator) {
//题目有可能是分数或整数,都当成分数来运算
var moleculeA = getMolecule(a)
var denominatorA = getDenominator(a)
var moleculeB = getMolecule(b)
var denominatorB = getDenominator(b)
//匹配运算符
switch (operator) {
case '+': {
var molecule = moleculeA * denominatorB + moleculeB * denominatorA
var denominator = denominatorA * denominatorB
var sum = TrueFract(molecule, denominator) //化为真分数或整数
return sum
break
}
case '-': {
var molecule = moleculeA * denominatorB - moleculeB * denominatorA
//如果分子运算结果是0,则结果为0
if (molecule == 0) {
var sum = 0
} else {
var denominator = denominatorA * denominatorB
var sum = TrueFract(molecule, denominator)
}
return sum
break
}
case '×': {
var molecule = moleculeA * moleculeB
var denominator = denominatorA * denominatorB
var sum = TrueFract(molecule, denominator)
return sum
break
}
case '÷': {
var molecule = moleculeA * denominatorB
var denominator = denominatorA * moleculeB
var sum = TrueFract(molecule, denominator)
return sum
break
}
default:
break
}
}
- 主要功能函数
- 随机生成问题集合并去除负数情况的函数 randomQus:传入的参数为问题数目,和运算数字不超过的最大数,声明三个数组分别存放问题字符串,运算符,正确答案,随机获取运算符个数和运算符类型,把它和随机获取的数字拼接起来,调用countIt函数计算结果,并返回存放问题和答案的对象。去除非负数用到了js的eval函数计算结果如果为负则把存放该式子的位置清空,重新生成式子
function randomQus(qusNum, maxNum) {
var countArry = [qusNum]//问题集合
var charArry = []//存放运算符
var correctAnswer = [qusNum]//存放正确答案
for (var k = 0; k < qusNum; k++) {
countArry[k] = ''
}
for (var j = 0; j < qusNum; j++) {
var n = getRandomNum(1, 3) //随机获得运算符的个数
var numberOfCount = new Array(n + 1)
var CountStr
for (var i = 0; i <= n; i++) {
var IsInt = getRandomNum(0, 1) //随机判断生成整数运算还是分数运算
var molecule = getRandomNum(1, maxNum)
var denominator = getRandomNum(1, maxNum)
if (IsInt) numberOfCount[i] = getRandomNum(1, maxNum)
//随机获得整数数组
else numberOfCount[i] = TrueFract(molecule, denominator) //随机获得分数数组
}
for (var i = 0; i < n; i++) {
var m = getRandomNum(0, 3) //随机选择运算符
countArry[j] += numberOfCount[i] + arry[m] //随机数字和随机运算符拼接
}
countArry[j] += numberOfCount[n]
//计算式子,若小于0,则把存放该式子的位置清空,重新生成式子
var molecule = getMolecule(countIt(countArry[j]))
var denominator = getDenominator(countIt(countArry[j]))
var sum = eval(molecule / denominator)
if (sum < 0) {
countArry[j] = ''
j--
} else {
var num = countIt(countArry[j])
correctAnswer[j] = num
console.log(num)
}
}
var info = {
question: countArry,
answer: correctAnswer
}
return info
console.log(info)
}
- 计算式子的函数countIt:利用split生成两个数组,一个存放运算符,一个存放运算数字,先计算乘除法,把运算后结果放回存放数字的数组的当前索引的位置,然后再计算加减法(写到这里我已经累了,,,,所以大家看一下注释就好)
//计算式子
function countIt(params) {
//console.log(eval(params));
var num = params.split(/[\+\-\×\÷]/)
var operator = params.match(/[\+\-\×\÷]/g)
//先计算乘法除法
for (var i = 0; i < operator.length; i++) {
//console.log(operator[i]);
//获取运算符
var char = operator[i]
if (char == '×' || char == '÷') {
operator.splice(i, 1)
//获取运算符左右的数字
var numL = num[i]
num.splice(i, 1)
var numR = num[i]
num.splice(i, 1)
var sum = playCount(numL, numR, char)
num.splice(i, 0, sum)
i--
}
}
//再计算加减法
while (operator != false) {
//获取运算符
var char = operator[0]
operator.splice(0, 1)
//获取运算符左右的数字
var numL = num[0]
num.splice(0, 1)
var numR = num[0]
num.splice(0, 1)
var sum = playCount(numL, numR, char)
num.splice(0, 0, sum)
}
return sum
}
- 页面交互函数
- playAll函数:点击生成题目按钮触发,获取题目数组把它用for循环展现在页面,并且把答案放进生成的span标签里面,但设为隐藏
function playAll() {
//将页面清空
document.getElementById('myQus').innerHTML = ''
document.getElementById('comment').innerHTML = ''
//题目数目
var qustionNum = document.getElementById('qustionNum').value
//运算范围,最大运算数字
var maxNum = document.getElementById('maxNum').value
var myquanstions = randomQus(qustionNum, maxNum)
console.log(myquanstions)
for (i = 0; i < qustionNum; i++) {
document.getElementById('myQus').innerHTML +=
'<br>' +
"<span class='qus'>" +
'第' +
(i + 1) +
'题:' +
myquanstions['question'][i] +
'=' +
'</span>' +
"<input type='text' class='myAnswer' />" +
"<span class='correctAnswer'>" +
myquanstions['answer'][i] +
'</span>'
}
}
- 计算答案个数和展现正确结果函数:点击完成答题按钮触发,将填入的答案与正确答案比较算出正确的个数和错误的个数,并把正确答案display设为inline-block
//计算填入答案的对错个数
document.getElementById('completedBtn').onclick = function () {
document.getElementById('comment').innerHTML = ''
var trueNum = 0
var falseNum = 0
var myAnswer = document.getElementsByClassName('myAnswer')
var correctAnswer = document.getElementsByClassName('correctAnswer')
for (i = 0; i < correctAnswer.length; i++) {
if (myAnswer[i].value == correctAnswer[i].innerHTML) {
trueNum++
} else {
falseNum++
}
correctAnswer[i].style.display = 'inline-block'
}
document.getElementById('comment').innerHTML +=
':)正确的个数为:' + trueNum + '<br>' + ' :(错误的个数为: ' + falseNum
}
- 生成文件函数
//下载问题
function downloadQ() {
var element = document.createElement('a')
var text = ''
var qus = document.getElementsByClassName('qus')
for (var i = 0; i < qus.length; i++) {
text += '\r\n ' + qus[i].innerHTML
}
var filename = 'Exercises.txt'
element.setAttribute(
'href',
'data:text/plain;charset=utf-8,' + encodeURIComponent(text)
)
element.setAttribute('download', filename)
element.style.display = 'none'
document.body.appendChild(element)
element.click()
}
//下载答案
function downloadA() {
var element = document.createElement('a')
var text = ''
var correctAnswer = document.getElementsByClassName('correctAnswer')
for (var i = 0; i < correctAnswer.length; i++) {
text += '\r\n ' + '第' + (i + 1) + '题答案:' + correctAnswer[i].innerHTML
}
var filename = 'Answers.txt'
element.setAttribute(
'href',
'data:text/plain;charset=utf-8,' + encodeURIComponent(text)
)
element.setAttribute('download', filename)
element.style.display = 'none'
document.body.appendChild(element)
element.click()
}
//下载结果
function downloadR() {
var element = document.createElement('a')
var text = document.getElementById('comment').innerHTML
var filename = 'Grade.txt'
element.setAttribute(
'href',
'data:text/plain;charset=utf-8,' + encodeURIComponent(text)
)
element.setAttribute('download', filename)
element.style.display = 'none'
document.body.appendChild(element)
element.click()
}
结果及测试
-
页面:是不是美美的???所以小学生用这个做肯定开心效率高
-
输入题目个数的最大数字,按生成题目按钮
- 填入答案并点击完成答题(这里我就以十道题为例吧),右边会出现正确答案,下面会有答题情况显示正确个数和错误个数
- 点击下载文件,生成问题,答案,答题情况文件
问题反思
(写到这里我已经半废了,那就长话短说吧!)
- 未能实现判断重复题目和括号运算
- 结对编程的好处就是一个人容易限定在一个思考的死区一直走不出,但是有了队友,思维方式不一样,两个脑子一起,讨论思考,比一个人写的效率高并且写得更好,这次编程就是这样子,我们两个一起思考分工,再整合在一起,而且合作过程比一个人写还开心,就是酱紫。
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 1600 | 1500 |
· Estimate | · 估计这个任务需要多少时间 | 1600 | 1500 |
Development | 开发 | 900 | 800 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 60 |
· Design Spec | · 生成设计文档 | 60 | 60 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 57 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 15 |
· Design | · 具体设计 | 30 | 30 |
· Coding | · 具体编码 | 800 | 780 |
· Code Review | · 代码复审 | 200 | 200 |
· Test | · 测试(自我测试,修改代码,提交修改) | 100 | 120 |
Reporting | 报告 | 150 | 120 |
· Test Report | · 测试报告 | 100 | 8- |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 40 |
合计 |