Four Operational Generators for Primary School

Keywords: Javascript github Vue encoding

Github project address: https://github.com/bravedreamer/test/tree/master/Arithmetic
Online preview: https://bravedreamer.github.io/test/Arithmetic/index.html

Project Partner: Wu Shangqian 3118004977 Wu Maoping 3118004976

1. Title Description

Implements a command line program that automatically generates four arithmetic titles for elementary schools (also using an image interface with similar functions).
Natural numbers: 0, 1, 2,....

True score: 1/2, 1/3, 2/3, 1/4, 1'1/2,....
Operators: +,, *,.
Brackets: (,).
Equal sign: =.
Separator: Space (used before and after the four operators and the equal sign).
Arithmetic expression:
e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),

Where e, e1 and e2 are expressions and n are natural numbers or true fractions.

Four operation titles: e = where e is an arithmetic expression.

Requirements:

  1. Using the -n parameter to control the number of generated titles, such as Myapp.exe-n 10, will generate 10 titles.

  2. Use the -r parameter to control the range of values (natural numbers, true fractions, and true fractional denominators) in the title, such as Myapp.exe -r 10
    Four arithmetic titles within 10 (excluding 10) will be generated.This parameter can be set to 1 or another natural number.This parameter must be given or the program will error and give help.

  3. The calculation process in the generated Title cannot produce negative numbers, that is, if there are subexpressions in the arithmetic expression such as e1_e2, E1 is greater than or equal to e2.

  4. If a subexpression such as e1_e2 exists in the generated title, the result should be a true fraction.

  5. There are no more than three operators in each topic.

  6. The Title generated by a single run of the program cannot be repeated, that is, no two titles can be transformed into the same title by a finite number of exchanges of arithmetic expressions of +and *For example, 23 + 45 = and 45 + 23 = are duplicate titles, and 6 *8 = and 8 *6 = are also duplicate titles.The two titles 3+ (2+1) and 1+2+3 are duplicated, because + is left-joined, 1+2+3 is equivalent to (1+2)+3, which is 3+ (1+2), or 3+ (2+1).However, 1+2+3 and 3+2+1 are not duplicate questions, because 1+2+3 is equivalent to (1+2)+3 and 3+2+1 is equivalent to (3+2)+1. They cannot be changed into the same topic through a finite exchange.
    The generated title is saved to the Exercises.txt file in the current directory of the executor in the following format:
    Four operations Title 1
    Four operations Title 2
    ......
    True fractions are input and output in the following format: three fifths of the true fraction is 3/5, and two and three eighths of the true fraction is 2'3/8.

  7. At the same time the questions are generated, the answers to all questions are calculated and stored in the Answers.txt file in the current directory of the executor in the following format:
    Answer 1
    Answer 2
    In particular, the operation of true fractions is shown in the following example: 1/6 + 1/8 = 7/24.

  8. The program should be able to support 10,000 questions.

  9. The program supports determining the right and wrong answers for a given title file and answer file and making quantitative statistics with the following input parameters:
    Myapp.exe -e .txt -a .txt
    The statistical results are output to the file Grade.txt in the following format:
    Correct: 5 (1, 3, 5, 7, 9)
    Wrong: 5 (2, 4, 6, 8, 10)
    The number 5 after':'denotes the number of correct/wrong titles, and the number of correct/wrong titles is enclosed in parentheses.For simplicity, assume that the titles entered are all sequentially numbered, canonical titles

2.PSP:
PSP2.1 Personal Software Process Stages Estimated time-consuming (minutes) Actual time consumed (minutes)
Planning plan 30 15
· Estimate Estimate how long this task will take 960 1365
Development Development 840 1320
· Analysis Needs analysis (including learning new technologies) 30 15
· Design Spec Generate design documents 20 20
· Design Review Design review (and review design documents with colleagues) 10 5
· Coding Standard Code specifications (develop appropriate specifications for current development) 10 10
· Design Specific design 10 10
· Coding Specific encoding 720 1230
· Code Review Code Review 10 10
· Test Test (self-test, modify code, submit changes) 30 20
Reporting Presentation 40 30
· Test Report . Test Report 20 10
· Size Measurement Calculate Workload 10 10
· Postmortem & Process Improvement Plan Post-mortem Summary and Process Improvement Plan 10 10
Total 910 1365
3. Performance Analysis

As the number of generated titles increases, the consumption of this part of the function increases with the number of titles.

createQuestion(){//Generate multiple titles
			  //Initialize data list
			 ...
			  
			  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. Ideas for implementation

5. Key Code Analysis

The functions of each function are basically implemented in one vue, which is more clear.

new Vue({
	...
	beforeCreate() {
				// read file
				FileReader.prototype.reading = function ({encode} = pms) {
					let bytes = new Uint8Array(this.result);    //Unsigned integer array
					let text = new TextDecoder(encode || 'UTF-8').decode(bytes);
					return text;
				};
				/* Override readAsBinaryString function */
				FileReader.prototype.readAsBinaryString = function (f) {
					if (!this.onload)       //If this does not override the onload function, create a common handling
						this.onload = e => {  //In this.onload function, complete the public processing
							let rs = this.reading();
							console.log(rs);
						};
					this.readAsArrayBuffer(f);  //The internal callback to this.onload method
				};
	},
	methods:{
		...
		   		  
		tableRowClassName({row, rowIndex}) {//Change table style
				  ...
			  },
			  
		createOperationArr(arr1,arr2){//Merge the generated array of operations and the array of operators
			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(){//Create operators and operands for a topic
			  let operation=[" + ", " − ", " × ", " ÷ ", " / "," = "]//Save Related Operators
			  let operationTime=Math.floor(Math.random() * (3 - 1+1)+1)//Number of operations
			  
			  //Random Generation Operator
			  let operationSymbol=[]//Save generated operators
			  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 takes random numbers between m-n [m,n]
			  //Random Generation Operator
			  let operationTagNumber=[]//Save generated operands
			  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(){//Generate multiple titles
			//Initialize data list
			...
			
			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){//Check for duplicates in generated titles
								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){//Infix expression to suffix expression
			let  symbolPriority = {//Determining Operational Priority
			  " # ": 0,
			  " + ": 1,
			  " − ": 1,
			  " × ": 2,
			  " ÷ ": 2,
			  " / ": 3
			}
			let operand=[]//Stack holding operands
			let operator=[]//Stack to save operators
			arr.unshift(" # ")//facilitate operation priority comparison
			
			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();//Eject "#"
			return operand;
		},
		getResult(arr){//Obtain calculation results
			let result=[]//Used to save results
			let count
			for(let i=0;i<arr.length;i++){
				if(typeof(arr[i])=='string'){
					....
				}else{
					result.push(arr[i])
				}
			}
			return result[0]
		},
		downloadQuestion(){//Download txt files for titles and answers
			let questionContent=""//Topic Content
			let answerContent=""//Answer Content
			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]+"\n"
					answerContent+=(i+1)+","+this.form.answerList[i]+"\n"
				}
				this.download(name1,questionContent)
				this.download(name2,answerContent)
			}else{
				this.$alert('The list of titles is empty. Please regenerate the titles', '', {
						  confirmButtonText: 'Determine',
						});
			}
		},
		
		download(filename, text){//Download TXT file
			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){//Upload Files
				this.fileList = [file]
				console.log('File selected beforeUpload')
				// Read data
				this.read(file);
				return false
			},
		read(f) {//Parse Uploaded Files
					let rd = new FileReader();
					rd.onload = e => {  
					//Within this.readAsArrayBuffer function, this.onload function is called back.Processing results here
						let cont = rd.reading({encode: 'UTF-8'});
					  this.fileData.push(cont)
						let  formerData = this.textData;
						this.textData = formerData + "\n" + cont;
					};
					rd.readAsBinaryString(f);
				  
			},
		  compareAnswer(){//Check the correctness of the answers to the uploaded questions and count the relevant results
			  let questionContent=[]//Save uploaded titles
			  let answerContent=[]//Save uploaded answers
			  let corretAnswer=[]//Save the correct answer
			  let corretList=[]//Save the serial number of the correct answer to the question
			  let wrongList=[]//Save the serial number of the incorrect answer to the title
			  let corret=""
			  let wrong=""
			  //Initialize data list
			  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("\n")
						  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])//Get the right answer
							  }
						  }
					  }else{
						  answerContent=this.fileData[i].split("\n")
						  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('Title not uploaded yet, please re-upload the title', '', {
							confirmButtonText: 'Determine',
						  });
			  }
		  },
		  getCorrectAnswer(str){//Get the right answer
			  let questionArr=str.split(" ")
			  questionArr.pop()//Pop up the last space cut
			  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. Test Run
  • The interface as a whole is as follows:

  • Control parameters can generate 10,000 titles or adjust the access to generated values

  • Download and upload files, no errors

  • homework correcting

7. Summary
  1. Team project collaboration is important. It's not messy to plan before you start
  2. Choosing the right tools for co-development, such as github
    3. Two people working together can interact with each other to come up with new ideas

Posted by VanPEP on Mon, 13 Apr 2020 10:55:17 -0700