• 小学四则运算生成器


    Github项目地址:https://github.com/bravedreamer/test/tree/master/Arithmetic
    在线预览:https://bravedreamer.github.io/test/Arithmetic/index.html

    项目合作者:吴尚谦 3118004977 吴茂平3118004976

    1.题目说明

    实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。
    自然数:0, 1, 2, …。

    真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
    运算符:+, −, ×, ÷。
    括号:(, )。
    等号:=。
    分隔符:空格(用于四则运算符和等号前后)。
    算术表达式:
    e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),

    其中e, e1和e2为表达式,n为自然数或真分数。

    四则运算题目:e = ,其中e为算术表达式。

    需求:

    1. 使用 -n 参数控制生成题目的个数,例如Myapp.exe -n 10,将生成10个题目。

    2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如Myapp.exe -r 10
      将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

    3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。

    4. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。

    5. 每道题目中出现的运算符个数不超过3个。

    6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
      生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
      四则运算题目1
      四则运算题目2
      ……
      其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

    7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
      答案1
      答案2
      特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

    8. 程序应能支持一万道题目的生成。

    9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
      Myapp.exe -e .txt -a .txt
      统计结果输出到文件Grade.txt,格式如下:
      Correct: 5 (1, 3, 5, 7, 9)
      Wrong: 5 (2, 4, 6, 8, 10)
      其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目

    2.PSP:
    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 30 15
    · Estimate · 估计这个任务需要多少时间 960 1365
    Development 开发 840 1320
    · Analysis · 需求分析 (包括学习新技术) 30 15
    · Design Spec · 生成设计文档 20 20
    · Design Review · 设计复审 (和同事审核设计文档) 10 5
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
    · Design · 具体设计 10 10
    · Coding · 具体编码 720 1230
    · Code Review · 代码复审 10 10
    · Test · 测试(自我测试,修改代码,提交修改) 30 20
    Reporting 报告 40 30
    · Test Report · 测试报告 20 10
    · Size Measurement · 计算工作量 10 10
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 10
    合计 910 1365
    3.效能分析

    随着生成的题目数量不断加大,这部分函数的消耗将会随着题目数量增大而不断增大

    createQuestion(){//生成多道题目
    			  //初始化数据列表
    			 ...
    			  
    			  let questionData=[]
    		  			for(let i=0;i<this.form.questionNum;){
    		  				let content=this.createQuestionInfo()
    		  				let answer=content.answer
    		  				let question=content.question
    		  				
    		  				if(answer>=0){
    		  					this.form.questionList[i]=question
    		  					this.form.answerList[i]=answer
    							let tag={}
    							tag.question=question+answer
    							tag.index=i+1
    							questionData[i]=tag
    		  					i++
    		  				}
    		  			}
    					this.tableData=questionData
    		  },
    
    4.实现思路

    5.关键代码分析

    各函数功能基本在一个vue内实现,较为清晰。

    new Vue({
    	...
    	beforeCreate() {
    				// 读取文件
    				FileReader.prototype.reading = function ({encode} = pms) {
    					let bytes = new Uint8Array(this.result);    //无符号整型数组
    					let text = new TextDecoder(encode || 'UTF-8').decode(bytes);
    					return text;
    				};
    				/* 重写readAsBinaryString函数 */
    				FileReader.prototype.readAsBinaryString = function (f) {
    					if (!this.onload)       //如果this未重写onload函数,则创建一个公共处理方式
    						this.onload = e => {  //在this.onload函数中,完成公共处理
    							let rs = this.reading();
    							console.log(rs);
    						};
    					this.readAsArrayBuffer(f);  //内部会回调this.onload方法
    				};
    	},
    	methods:{
    		...
    		   		  
    		tableRowClassName({row, rowIndex}) {//改变表格样式
    				  ...
    			  },
    			  
    		createOperationArr(arr1,arr2){//合并已生成的运算数数组和运算符数组
    			let operationArr=[]
    			let question=""
    			
    			for(let i=0;i<arr2.length;i++){
    				question+=(arr1[i]+arr2[i])
    				operationArr.push(arr1[i])
    				operationArr.push(arr2[i])
    				if(i==(arr2.length-1)) {
    					question+=arr1[(i+1)]
    					operationArr.push(arr1[(i+1)])
    					}
    			}
    		   return {operationArr,question}
    		},
    		
    		createQuestionInfo(){//创建一道题目的运算符和运算数
    			  let operation=[" + ", " − ", " × ", " ÷ ", " / "," = "]//保存相关运算符
    			  let operationTime=Math.floor(Math.random() * (3 - 1+1)+1)//运算次数
    			  
    			  //随机生成运算符
    			  let operationSymbol=[]//保存生成的运算符
    			  for(let k=0;k<operationTime;){
    				  let i=Math.floor(Math.random() * (4 - 0+1))
    				  
    				  if(i==4){
    					  if(operationSymbol.length>0&&operationSymbol[operationSymbol.length-1]==operation[i]){
    						  
    					  }else{
    						  operationSymbol[operationSymbol.length]=operation[i]
    					  }
    				  }else{
    					  operationSymbol[operationSymbol.length]=operation[i]
    					  k++
    				  }
    				  
    			  }
    			  
    			  // Math.floor(Math.random()*(n-m+1))+m 取m-n之间的随机数 [m,n]
    			  //随机生成运算数
    			  let operationTagNumber=[]//保存生成的运算数
    			  for(let k=0;k<=operationSymbol.length;){
    				  let last=k-1
    				  
    				  if(k>0&&(operationSymbol[last]==operation[4]||operationSymbol[last]==operation[3])){
    					  let t=Math.floor(Math.random() * (Number(this.form.max) - Number(this.form.min))) +Number(this.form.min)
    					  if(operationSymbol[last]==operation[4]&&t!=0&&operationTagNumber[last]<=t){
    						  operationTagNumber[k]=t
    						  k++	
    					  }
    					  if(t!=0&&operationSymbol[last]==operation[3]){
    						  operationTagNumber[k]=t
    						  k++
    					  }
    				  }else{
    					  operationTagNumber[k]=Math.floor(Math.random() * (Number(this.form.max) - Number(this.form.min))) +Number(this.form.min)
    					  k++
    				  }
    			  }
    			  
    			  let content=this.createOperationArr(operationTagNumber,operationSymbol)
    			  let operationArr=content.operationArr
    			  let question=content.question
    			  question+=operation[5]
    			  
    			  operationArr=this.getRPN(operationArr)
    			  let answer=this.getResult(operationArr)
    			  
    			  return{question,answer}
    		},
    		createQuestion(){//生成多道题目
    			//初始化数据列表
    			...
    			
    			let questionData=[]
    					for(let i=0;i<this.form.questionNum;){
    						...
    
    						for(let j=0;j<this.form.questionList.length;j++){
    							if(this.form.questionList[j]==question){//检查生成的题目是否重复
    								isRepeat=true
    								break;
    							}else{
    								isRepeat=false
    							}
    						}
    						if(answer>=0&&!isRepeat){
    							this.form.questionList[i]=question
    							this.form.answerList[i]=answer
    						  let tag={}
    						  tag.question=question+answer
    						  tag.index=i+1
    						  questionData[i]=tag
    							i++
    						}
    					}
    				  this.tableData=questionData
    		},
    		getRPN(arr){//中缀表达式转后缀表达式
    			let  symbolPriority = {//确定运算优先级
    			  " # ": 0,
    			  " + ": 1,
    			  " − ": 1,
    			  " × ": 2,
    			  " ÷ ": 2,
    			  " / ": 3
    			}
    			let operand=[]//保存运算数的栈
    			let operator=[]//保存运算符的栈
    			arr.unshift(" # ")//方便进行运算优先级比较
    			
    			for(let i=0;i<arr.length;i++){
    				if(typeof(arr[i])=="number"){
    				   operand.push(arr[i])
    				}else{
    					switch (true){
    						case (arr[i]==' ( '||operator.slice(-1)[0]==' ( '):
    							operator.push(arr[i]);
    							break;
    						case (arr[i] == ' ) '):
    							do{
    								 operand.push(operator.pop());
    							}while(operator.slice(-1)[0] != " ( ")
    							operator.pop()
    							break;
    						default:
    							if(operator.length == 0){
    									operator.push(arr[i]);
    								}else if(symbolPriority[operator.slice(-1)[0]]>=symbolPriority[arr[i]]){
    									do{
    									  operand.push(operator.pop());
    									}while (symbolPriority[arr[i]]<=symbolPriority[operator[operator.length-1]])
    								  operator.push(arr[i]);
    								}else {
    									 operator.push(arr[i]);
    								}
    							break;
    					}
    				}
    			}
    			operator.forEach(function(){
    					operand.push(operator.pop());
    				});
    			operator.pop();//弹出"#"
    			return operand;
    		},
    		getResult(arr){//获取计算结果
    			let result=[]//用于保存结果
    			let count
    			for(let i=0;i<arr.length;i++){
    				if(typeof(arr[i])=='string'){
    					....
    				}else{
    					result.push(arr[i])
    				}
    			}
    			return result[0]
    		},
    		downloadQuestion(){//下载题目和答案的txt文件
    			let questionContent=""//题目内容
    			let answerContent=""//答案内容
    			if(this.form.questionList.length!=0){
    				let name1="Exercises"
    				let name2="Answers"
    				for(let i=0;i<this.form.questionList.length;i++){
    					questionContent+=(i+1)+"、"+this.form.questionList[i]+"
    "
    					answerContent+=(i+1)+"、"+this.form.answerList[i]+"
    "
    				}
    				this.download(name1,questionContent)
    				this.download(name2,answerContent)
    			}else{
    				this.$alert('题目列表为空,请重新生成题目', '', {
    						  confirmButtonText: '确定',
    						});
    			}
    		},
    		
    		download(filename, text){//下载TXT文件
    			let element = document.createElement('a');
    			 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();
    			
    			 document.body.removeChild(element);
    		},
    		beforeUpload(file){//上传文件
    				this.fileList = [file]
    				console.log('选择了文件beforeUpload')
    				// 读取数据
    				this.read(file);
    				return false
    			},
    		read(f) {//解析上传过来的文件
    					let rd = new FileReader();
    					rd.onload = e => {  
    					//this.readAsArrayBuffer函数内,会回调this.onload函数。在这里处理结果
    						let cont = rd.reading({encode: 'UTF-8'});
    					  this.fileData.push(cont)
    						let  formerData = this.textData;
    						this.textData = formerData + "
    " + cont;
    					};
    					rd.readAsBinaryString(f);
    				  
    			},
    		  compareAnswer(){//检查上传过来的题目的答案的正确性并统计相关结果
    			  let questionContent=[]//保存上传过来的题目
    			  let answerContent=[]//保存上传过来的答案
    			  let corretAnswer=[]//保存正确答案
    			  let corretList=[]//保存题目正确答案的序号
    			  let wrongList=[]//保存题目错误答案的序号
    			  let corret=""
    			  let wrong=""
    			  //初始化数据列表
    			  this.form.questionList=[]
    			  this.form.answerList=[]
    
    			  if(this.fileData.length!=0){
    				  for(let i=0;i<this.fileData.length;i++){
    					  if(this.fileData[i].includes("=")){
    						  questionContent=this.fileData[i].split("
    ")
    						  for(let k=0;k<questionContent.length;k++){
    							  for(let n=0;n<questionContent[i].length;n++){
    								  if(questionContent[k][n]=="、")
    									  questionContent[k]=questionContent[k].substr(n+1)
    							  }
    							  if(questionContent[k]==""){
    								  questionContent.pop()
    							  }else{
    								  corretAnswer[k]=this.getCorrectAnswer(questionContent[k])//获取正确答案
    							  }
    						  }
    					  }else{
    						  answerContent=this.fileData[i].split("
    ")
    						  for(let j=0;j<answerContent.length;j++){
    							  for(let m=0;m<answerContent[j].length;m++){
    								  if(answerContent[j][m]=="、")
    									  answerContent[j]=answerContent[j].substr(m+1)
    							  }
    							  if(answerContent[j]!=""){
    								  answerContent[j]=Number(answerContent[j])
    							  }else{
    								  answerContent.pop()
    							  }
    						  }
    					  }
    				  }
    				  let questionData=[]
    				  for(let n=0;n<answerContent.length;n++){
    					  if(answerContent[n]==corretAnswer[n]){
    						  corretList.push(n+1)
    						  corret+=(n+1)+","
    					  }else{
    						  wrongList.push(n+1)
    						  wrong+=(n+1)+","
    					  }
    					  let tag={}
    					  tag.question=questionContent[n]+answerContent[n]
    					  tag.index=n+1
    					  questionData[n]=tag
    				  }
    				  this.tableData=questionData
    				  
    				  this.corretList=corretList
    				  this.wrongList=wrongList
    				  
    				  corret=corret.substr(0, corret.length-1)
    				  wrong=wrong.substr(0, wrong.length-1)
    				  corret="Correct:"+corretList.length+"("+corret+")"
    				  wrong="Wrong:"+wrongList.length+"("+wrong+")"
    				  this.corret=corret
    				  this.wrong=wrong
    			  }else{
    				  this.$alert('暂未上传题目,请重新上传题目', '', {
    							confirmButtonText: '确定',
    						  });
    			  }
    		  },
    		  getCorrectAnswer(str){//获取正确答案
    			  let questionArr=str.split(" ")
    			  questionArr.pop()//弹出最后切到的空格
    			  for(let i=0;i<questionArr.length;i++){
    				  if(questionArr[i]=='='){
    					  questionArr.splice(i,1)
    				  }else{
    					  if(questionArr[i]=="/"||isNaN(Number(questionArr[i]))){
    						  questionArr[i]=" "+questionArr[i]+" "
    					  }else{
    						  questionArr[i]=Number(questionArr[i])
    					  }
    				  }
    			  }
    			  questionArr=this.getRPN(questionArr)
    			  let corretAnswer=this.getResult(questionArr)
    			  return corretAnswer
    		  }
    	},
      })
    
    
    6.测试运行
    • 界面整体如下:
      在这里插入图片描述

    • 控制参数可实现10000道题目生成,也可调节生成数值访问在这里插入图片描述

    • 下载与上传文件均实现,无错误
      在这里插入图片描述

    • 批改作业
      在这里插入图片描述

    7. 小结
    1. 团队项目合作比较重要,先做好计划再动手不会很乱
    2. 选择适当的工具有利于共同开发,比如github
      3.两人合作可以交互出新颖的想法
  • 相关阅读:
    关于换行的问题
    Ubuntu 18.4 查看CUDNN版本
    opencv bgr转rgb
    python读写json文件
    timm库,PyTorchImageModels,简称timm,是一个巨大的PyTorch代码集合
    用SQL创建数据库登录用户
    《dsu on tree》深度剖析
    C#调用DLL报错:试图加载格式不正确的程序
    【项目】项目147
    【项目】项目148
  • 原文地址:https://www.cnblogs.com/brave-dreamer/p/12693333.html
Copyright © 2020-2023  润新知