For a long time not to write, this time is the point of recent business related 😂. This one was also written on and off for three months (hello! To make a small program on top of a small program, the whole focus is divided into two parts:

  • 1. Input control;
  • 2. Calculation.

The input control is to make sure that the final calculation that we compute is a valid one. Because eval is unsafe, the applet disables this method, and we have to implement a simple ‘+-*/’ ourselves. As shown in figure:

Input control:

We divide the formula into two parts, one is the real formula for calculation, and the other is the display formula for users to view and understand.

  1. The real arithmetic:
  • Make sure the first digit of the formula is a number. If the input is a symbol, add zeros in front of it.
  • Make sure the last digit is a number, if not throw an error
  • Ensure that the expression is of the ‘num-symbol – numeral’ format. When entering a symbol, replace the current last digit if it is also a symbol
  • If the current initial default is 0, the first numeric input is replaced with real input.
  1. Display the formula:
  • Replace the ‘*’, ‘/’ symbols, into easy to watch ‘×’, ‘÷’;
  • If a symbol is entered in the first place, zero is added before it;

When input, the real formula and the displayed formula are changed together to ensure that the formula is valid.

Formula to calculate

Take 2+5 x 6-10 as an example.

The general idea is that the formatted valid arithmetic string is divided into significant digits and symbolic parts based on symbols, as shown in the following example:

"2 + 5 * 6-10"= > ['2'.'+'.'5'.The '*'.'6'.The '-'.'10']
Copy the code

Iterate the obtained string again, calculate according to the priority, and divide the calculation in the formula into the smallest formula for calculation.

1. Write a function for simple ‘+-*/’ calculations.

/* * @param num1 Enter the number 1 * @param num2 enter the number 2 */
signCompute(sign:string, num1:number, num2:number): number {
  switch(sign){
    case '+':
      return num1 + num2;
    case The '-':
      return num1 - num2;
    case The '*':
      return Math.round(num1 * num2);
    case '/':
      return Math.round(num1 / num2);
    default:
      return num1
  }
}
Copy the code

2. Convert flat arithmetic strings into valid arrays and characters between symbols into numbers.

/* * converts a string to a valid array * @param formula A valid string formula */
formulaToArray(formula: string) {
  const length = formula.length
  let str = ' ' // Stores characters, and if a symbol is encountered, pushes the stored string into the array
  const regex = / * \ / + - / // Check whether it is a symbol
  const formulaList = [] // The correct array to return
  for (let index=0; index < length; index++){ // Start iterating over the string
    const item = formula[index]
    // If the current element is a symbol, push the number stored in STR and the current symbol into the array, empty STR, and go to the next loop.
    if (regex.test(item)) {
      formulaList.push(str)
      formulaList.push(item)
      str = ' '
      continue
    }
    // When the current character is not a symbol, merge the current character into STR
    str += item
    // The current character is the last character, and the string stored in STR is pushed into the array
    if (index == length - 1) formulaList.push(str) 
  }
  return formulaList
}
Copy the code

3. Iterate through the array of legitimate formulas obtained by 2 and calculate.

If only “+-*/” is calculated, the formula has two priorities, so it needs to be traversed twice:

  • For the first time, multiply and divide the first priority. From left to right to traverse the word group, encountered multiplication and division formula for calculation, and the result to replace the original formula position. For example, “5*6” is calculated on the first pass and 30 is obtained, which changes the original equation into “2+30-10”.
  • The second pass computes the “+-” of the second priority from left to right. 2+30-10 ➡️ 32-10 ➡️ 22.
  • If an invalid argument is encountered, the error is thrown and then caught in the outer layer.
/* * @param list */
computeHasPriority(list: Array<string>) {
  // Calculate the first priority for the first traversal
  for (let index = 0; index < list.length; index++) {
    const item = list[index]
    const preIndex = index - 1 // Indicates the subscript of the previous character
    const lastIndex = index + 1 // Points to the subscript of the next character
    // If the current element is not "*" or "/", the current loop ends
    if(item ! = =The '*'&& item ! = ='/') continue
    // A divisor of 0 throws an error
    if (item == '/' && list[lastIndex] == '0') throw new Error('Divisor can't be zero.')
    // Evaluate to get the value (the current index refers to the symbol not)
    const res = this.signCompute(item, Number(list[preIndex]), Number(list[lastIndex]))
    // Replace the last character of the symbol bit with the resulting value
    list[lastIndex] = res.toString()
    // Remove the symbol bit and the character before the symbol bit
    list.splice(preIndex, 2)
    // The current index is moved forward by two places, repointing to the value just obtained
    index = index - 2
  }
  // The second pass computs the second priority, and returns the result if necessary
  for (let index = 0; index < list.length; index++) {
    const item = list[index]
    const preIndex = index - 1
    const lastIndex = index + 1
    // If the current element is not "+" or "-", the current loop ends
    if(item ! = ='+'&& item ! = =The '-') continue
    const res = this.signCompute(item, Number(list[preIndex]), Number(list[lastIndex]))
    list[lastIndex] = res.toString()
    // If the symbol bit is the last character position, the calculation result is returned directly
    if (lastIndex == list.length - 1) return res.toString()
    // Remove the symbol bit and the character before the symbol bit
    list.splice(preIndex, 2)
    // The current index is moved forward by two places, repointing to the value just obtained
    index = index - 2
  }
  // Return the number if it has only one digit
  if (list.length == 1) return Number(list[0])
  else return 0
}
Copy the code

use

const formula = '2 + 5 * 6-10'
const formulaList = formulaToArray(formula)
try{
  const result = computeHasPriority(formulaList)
  return true
}catch(e) {
  console.log(e)
  this.setData({ error: 'Your calculation is not correct.' })
  return false
}
Copy the code