Elementary School Four Arithmetic Question Generator (JavaScript)

1. Introduction

  • This program is a program generated by four operations of a pupil with a graphical interface
  • Author: Yu Shengyuan Wang trunk
  • github address: https://github.com/yushengyuan123/caculation
  • Technical framework: JavaScript + electron + node.js

2. Effectiveness analysis

  • The biggest performance cost of the whole process should be the generation of answers. The generation of answers requires the conversion of infix expressions to suffixes, and then processing the suffix expressions to get the answers. The whole process requires more complicated logical judgments.  

Third, the design process

  • Due to the cooperation of two people, the project is mainly divided into two divisions of labor, one person is responsible for file operations and page html writing, and the other person is responsible for writing business logic related to the calculation formula, including the random generation and result generation of the calculation formula. Encapsulate all the functions related to the calculation formulas, and provide them to the students responsible for the documents and page writing to call. The basic relationship diagram is as follows:
    img
  • Due to the use of the graphical interface, no console commands are used in order to repeat.
  • The graphical interface is shown below:
    img

Four, code description (only the core code in the business is shown here)

  • Randomly generated computational algorithm.
const _isNumber = require('../share/utils').isNumber
const getRandom = require('../share/utils').getRandom
const getResult = require('./stack')
const print = require('../share/utils').printf
const _ = require('underscore')

const operator = ['+', '-', '*', '÷']

const _toString = Object.prototype.toString

/**
 * 随机获得一个运算符
 * @returns {string}
 */
function getOperator() {
    return operator[Math.floor(Math.random() * (3 + 1))]
}

/**
 *
 * @param qus 提交的答案
 * @param ans 正确的答案
 * @returns {*}
 */
function verifyAnswer(qus, ans) {
    let statistic
    if (_toString.call(qus) !== '[object Array]' || _toString.call(ans) !== '[object Array]') {
        throw new Error('Please dont set the poison for my code')
    }
    return statistic
}


//判断运算式子结果是否大于0
const _positive = function (expression) {
    return getResult(expression) >= 0
}

/**
 * 生成答案
 * @param qus 传入生成计算式子返回的数组
 * @returns {['1', '2', ...]}
 */
const answer = function (qus) {
    if (_toString.call(qus) !== '[object Array]') {
        throw new Error(qus + 'is not a Array')
    }
    let answer = []
    for (let i = 0; i < qus.length; i++) {
        let temp = qus[i].split('=')[0]
        temp = temp.substring(0, temp.length - 1)
        answer.push(getResult(temp))
    }
    return answer
}


/**
 * 生成计算表达式
 * @param number 传入你要生成多少条式子的数量
 * @returns {['1 + 1 =', ...]}
 */
const createExpression = function(number) {
    if(!_isNumber(number)) {
        throw new Error(`The ${number} is not a number type, please check again!`)
    }
    let result = []
    let operands = null
    let output = ''
    let index = 0
    while(index !== number) {
        operands = Math.floor(Math.random()* 3 + 1)
        switch(operands) {
            case 1: {
                output = `${getRandom()} ${getOperator()} ${getRandom()}`
                if(_positive(output)) {
                    result.push(`${output} = `)
                    index++
                }
                break
            }
            case 2: {
                output = `${getRandom()} ${getOperator()} ${getRandom()} ${getOperator()} ${getRandom()}`
                if(_positive(output)) {
                    result.push(`${output} = `)
                    index++
                }
                break
            }
            case 3: {
                output = `${getRandom()} ${getOperator()} ${getRandom()} ${getOperator()} ${getRandom()} ${getOperator()} ${getRandom()}`
                if(_positive(output)) {
                    result.push(`${output} = `)
                    index++
                }
                break
            }
            default: {
                throw new Error('operands is not in the range of 3')
            }
        }
    }
    return result
}

module.exports = {
    createExpression: createExpression,
    answer: answer
}
  • Randomly generate fractions or natural numbers
//判断是假分数还是带分数
const bandFraction = function (fraction) {
    let temp = fraction.split('/')
    let result = Number(temp[0]) / Number(temp[1])
    if (result > 1) {
        return `${Math.floor(result)}^${Number(temp[0] - Number(Math.floor(result)) * temp[1] + 1)}/${temp[1]}`
    } else if (result === 1){
        return `1`
    } else {
        return fraction
    }
}

//随机返回分数或者是整数
//Math.floor(Math.random()*(m-n+1)+n)
const getRandom = function() {
    //随机决定是生成整数还是分数1表示整数,0表示分数
    let isZ = Math.round(Math.random())
    if(isZ) {
        return Math.floor(Math.random() * 9 + 1)
    } else {
        let Molecule = Math.ceil(Math.random() * 9 + 1)
        let Denominator = Math.ceil(Math.random() * (Molecule * 10  - 1 + 1) + 1)
        return bandFraction(`${Denominator}/${Molecule}`)
    }
}

The idea of ​​generating ideas: the getRandom function decides whether to randomly generate a fraction or a natural number, and uses a variable that randomly generates 0 and 1 to decide which one to generate, 0 generates a fraction, and 1 generates a natural number. When generating a calculation expression, a random variable that generates 1-3 is also used to determine the length of the calculation formula. 1 means two operands and one calculation symbol, 2 means three operands and two operators, and 3 analogy. Each operand that generates a calculation expression calls getRandom once to generate a random operand. This is the overall idea.

  • Calculation result rendering code
/**
 * 符号优先级比较
 * @param operator_one
 * @param operator_two
 * @returns {boolean}
 */
const operatorRank = function (operator_one, operator_two) {
    if(operator_one === void 0) {
        throw new Error('you not have a expression')
    }
    if (operator_two === undefined) {
        return true
    }
    if (operator_one === '/' || operator_one === '*') {
        return !(operator_two === '/' || operator_two === '*');
    } else if (operator_one === '+' || operator_one === '-'){
        return operator_two === ')' || operator_two === '(';
    } else if (operator_two === ')' || operator_two === '(') {
        return false
    }
}

const changeFormat = function (array) {
    let freeback = array.slice(0)
    for (let i = 0; i < array.length; i++) {
        if (array[i] === '÷') {
            freeback[i] = '/'
        }
        if (array[i].length > 1 && array[i].indexOf('/') !== -1) {
            if (array[i].indexOf('^') !== -1) {
                let temp = freeback[i].split('/')
                let one = temp[0].split('^')
                freeback[i] = Number(one[0]) + Number(one[1] / temp[1])
            } else {
                let temp = freeback[i].split('/')
                freeback[i] = temp[0] / temp[1]
            }
        }
    }
    return freeback
}

/**
 * 计算器
 * @param expressionArray
 * @returns {[]}
 */
const counter = function (expressionArray) {
    expressionArray = changeFormat(expressionArray.split(' '))
    let outStack = []
    let operatorStack = []
    for (let i = 0; i < expressionArray.length; i++) {
        if (typeof Number(expressionArray[i]) == "number"
            && !isNaN(Number(expressionArray[i]))) {
            outStack.push(expressionArray[i])
        } else if (expressionArray[i] === '(') {
            operatorStack.push(expressionArray[i])
        } else if (expressionArray[i] === '+'
            || expressionArray[i] === '-'
            || expressionArray[i] === '*'
            || expressionArray[i] === '/') {
            if (operatorRank(expressionArray[i], operatorStack[operatorStack.length-1])) {
                operatorStack.push(expressionArray[i])
            } else {
                outStack.push(operatorStack.pop())
                while (!operatorRank(expressionArray[i], operatorStack[operatorStack.length-1])) {
                    outStack.push(operatorStack.pop())
                }
                operatorStack.push(expressionArray[i])
            }
        } else if (expressionArray[i] === ')') {
            while (operatorStack[operatorStack.length-1] !== '(') {
                outStack.push(operatorStack.pop())
            }
            if (operatorStack[operatorStack.length-1] === '(') {
                operatorStack.pop()
            }
        }
        if (i === expressionArray.length - 1) {
            while (operatorStack.length !== 0) {
                outStack.push(operatorStack.pop())
            }
        }
    }
    return outStack
}

/**
 * 答案产生器
 * @param suffix
 * @returns {[]}
 */
const getResult = function (suffix) {
    suffix = counter(suffix)
    let resultStack = []
    for (let i = 0; i < suffix.length; i++) {
        if (typeof Number(suffix[i]) == "number"
            && !isNaN(Number(suffix[i]))) {
            resultStack.push(Number(suffix[i]))
        } else {
            switch (suffix[i]) {
                case '+': {
                    resultStack.push(Number(resultStack.pop()) + Number(resultStack.pop()))
                    break
                }
                case '-': {
                    let reduce = Number(resultStack.pop())
                    let beReduce = Number(resultStack.pop())
                    resultStack.push(beReduce - reduce)
                    break
                }
                case '*': {
                    resultStack.push(Number(resultStack.pop()) * Number(resultStack.pop()))
                    break
                }
                case '/': {
                    let reduce = Number(resultStack.pop())
                    let beReduce = Number(resultStack.pop())
                    resultStack.push(beReduce / reduce)
                    break
                }
                default: {
                    throw new Error('illegal symbol ')
                }
            }
        }
    }
    return resultStack[0]
}

module.exports = getResult
  • The answer generates the following ideas:
    • In the beginning, the eval () method was adopted for convenience. It can parse a calculation formula into a calculation book result, but he has several defects. The first is that it can be used for code injection, which is very dangerous. Second, if the calculation formula has a fraction, because the fraction is essentially a division formula with priority, directly using eval, he can not recognize whether you are a fraction or a simple division. Third, it's too brainless, and it seems to be less technical.
    • Taking into account these factors, I decided to give up the use of this method, although he is very convenient, but there are many drawbacks. Finally, I wrote a calculator myself, which is to convert an infix expression into a suffix expression, and use the suffix expression to calculate. Finally, the result is obtained. The above code is to convert the infix expression into a suffix expression and finally process the calculation result of the suffix expression. The process of infix to suffix conversion is not introduced here.

Five, test operation

  • Randomly generated 10 questions test results are shown in the figure:
    img

The ^ in the title represents the mixed score. I did 10 questions manually, and three of them wrote an answer to see if the system can automatically check. Found that the test results are correct.

  • When submitting a question, a question file and answer file and the output of statistical results will be generated correspondingly, as shown in the following screenshot:
    img
  • The screenshots of the three files are as follows:
    • img
    • img
    • img

The final statistical results are consistent with the completion of the interface questions. 7 questions are correct and 3 questions are incorrect.

6. PSP form record

PSP2.1 Personal Software Process Stages Estimated time (minutes) Actual time (minutes)
Planning plan 1440 1500
· Estimate · Estimate how much time this task will take 1440 1500
Development Development 1000 1100
· Analysis · Needs analysis (including learning new technologies) 60 40
· Design Spec · Generate design documents 0 0
· Design Review · Design review (review design documents with colleagues) 60 80
· Coding Standard · Code specifications (making appropriate specifications for current development) 60 60
· Design · Specific design 60 60
· Coding · Specific coding 1000 1100
· Code Review · Code review 200 250
· Test · Test (self-test, modify code, submit changes) 200 250
Reporting report 0 0
· Test Report · testing report 0 0
· Size Measurement · Calculate workload 1400 1200
· Postmortem & Process Improvement Plan · Summary afterwards, and propose a process improvement plan 60 60
total 1400 1500

7. Project Summary

  • Yu Shengyuan: This project is once again familiar with the use of the packaging of the electron framework. The most challenging part of the entire project is the algorithm writing of the infix expression to suffix expression. The js implementation is simpler than the c implementation.
  • Wang Shugan: Probably because we have more cooperation, this project started quickly, and after a while, the division of labor and functional interfaces were quickly determined. After the general framework is determined, we don't need much communication later. We all started to develop our own parts independently, and finally integrated them directly for program testing and some minor modifications.

Guess you like

Origin www.cnblogs.com/wsg1111/p/12675194.html