A few days ago there was an interview topic: Calculate the string "1 + (5-2) * 3", the result is 10, cannot use Eval (). Today, we introduce a method to solve the problem by using a pressure stack, in fact our calculator principle is the same.
1 Analysis Topics
(1) If the calculation of the "1+2" of the two-digit operation, it is relatively simple, can be directly decomposed "character number", cast to float type, and then according to the middle operator subtraction on the line. This problem is difficult in the need to complicate the calculation of the high-level operators have priority.
(2) Usually when we are calculating, we are actually doing an operation between two numbers, and then we calculate the finished result and the other number. For example, "1 + 2 + 4", the first step is calculated after 1+2=3, and then calculated results 3 and 4 are added, the final result 7.
(3) If we can "1+2+4" the formula as a well-handled list:
The string formula "1+2+4" is processed into: [' 1 ', ' + ', ' 2 ', ' + ', ' 4 ']
Then we can calculate the result by pressing the stack. First set up two lists (stacks), hold numbers and operators, and then traverse [' 1 ', ' + ', ' 2 ', ' + ', ' 4 ']:
TraverseList of processed formulas: [' 1 ', ' + ', ' 2 ', ' + ', ' 4 ']First time:Get the number ' 1 ', convert to float, put into the digital stack: digital stack: [1.0,] operator stack: []Second time:Get operator ' + ', put into operator stack: number stack: [1.0,] operator stack: [' + ',]third time:Get the number ' 2 ', convert to float, put into the digital stack: digital stack: [1.0, 2.0] operator stack: [' + ',]Fourth time:To get the operator ' + ', it should be noted that the last digit of the operator stack is also the ' + ', and now comes a ' + ' sign, indicating that the precedence level of the next two operators is the same. Since the priority level is the same, the arithmetic rule tells us that we should calculate from left to right, right? Therefore, the operator ' + ' is no longer placed in the stack. Instead: (1) pop up the last two digits of the digital stack, i.e. 2.0 and 1.0; (2) The last operator in the pop-up operator stack ' + '; (3) computes the number and operation of the popup, i.e. 2.0 + 1.0 = 3.0; (4) puts 3.0 into the digital stack instead of the previous 1.0 and 2.0 ; that is: Digital stack: [3.0,] operator stack: [] Do not forget, we get the fourth time operator ' + ', at this time, if the operator stack, after the last operation of the operator ' + ', there are other operators, then we should also the operator stack of the final operator and this time the resulting operator ' + ' Compare to determine if it is the same level. If the same level still has to continue playing the stack, continue the operation. Not at the same level, the operator should be put into the stack. Now that our operator stack is empty, we should put the operator ' + ' into the operator stack, that is: the number stack: [3.0,] operator stack: [' + ',] so the fourth time is done.Fifth time:Get the number ' 4 ', convert to float, put into the digital stack: digital stack: [3.0, 4.0] operator stack: [' + ',] so far we have traversed the list of equations: [' 1 ', ' + ', ' 2 ', ' + ', ' 4 '], but there are elements in the stack of numbers and operators. Then we should pop up the last two digits 4.0 and 3.0, and the last operator ' + ', then perform the operation, get 7.0, and replace the original number stack of 4.0 and 3.0, namely: the Digital stack: [7.0,] operator stack: [] The final result is the first element in the digital stack: 7.0.
By describing the process of calculating "1+2+4", we summarize the two key points that this method calculates:
The first point: The calculation is processed into a list form. such as: ' -1-2* (( -2+3) + ( -2/2) ' should be processed into: ['-1 ', '-', ' 2 ', ' * ', ' (', ' (') ' (', '-2 ', ' + ', ' 3 ', ') ', ' + ', ' (', '-2 ', '/', ' 2 ', ') ', ') '.
The second focus: build two stacks, digital stacks, and operator stacks. Iterate through the list of formulas, (the elements in the list before it), put the numbers in the digital stack, and put the operators in the operator stack. However, the last operator in the operator stack needs to be compared to the currently obtained operator to determine whether the stack should be performed or directly into the stack.
2 Summary algorithm
Through the analysis in 1 we can roughly sort out the following algorithms:
1 sort the calculation into a list formula_list. 2 Loop [for the convenience of description, we loop here called Loop 1], and then take out the list of elements e (element abbreviation). If e is a number: Join the digital stack num_stack to get the next element E. Else E is not a number (that is, an operator): while True: (This is a constant loop, which is to compare the precedence of the operator and the last operator in the stack of operators from the list of formulas) if the operator stack op_stack is empty: operator E unconditionally joins the operator stack, And gets the next element, E, if the operator stack is not empty: The last operator of the operator stack is taken out and the current operator E compares, a decision is drawn. The decision is divided into [into the stack, out of the stack, 0] three kinds. If the decision is in the stack: Add the operator to the operator stack and get the next element e if the decision is out of the stack: the last two digits in the pop-up number stack, the last operator in the operator stack. calculation, replacing the original number with the result. Fallback to while true loop if the decision is 0, this situation specifically indicates that the last element of the operator stack is "(" and the currently acquired element is ")": The last operator in the pop-up operator Stack "(", and discard the current element is ")", get the next element 3 There may be a problem with the above processing: when the decision is 0, we pop the last operator in the operator stack "(", and discard the current element is ")" to get the next element, and if the list does not have the next element at this time? At this point there is neither the next element nor the continuation operation, " Loop 1" ends, but there may be elements in the digital stack and the operator stack. And the operators must be of the same level. So you should always do the stack operation until there are no operators in the operator stack. The final result is the first element of the digital stack.
In the above analysis we abstracted several functions:
(1) A function that calculates the result of ' two numbers and operators ' as a stack.
(2) A function that determines whether an element is a number or an operator.
(3) A function that processes a calculation into a list form. such as: ' -1-2* (( -2+3) + ( -2/2) ' should be processed into: ['-1 ', '-', ' 2 ', ' * ', ' (', ' (') ' (', '-2 ', ' + ', ' 3 ', ') ', ' + ', ' (', '-2 ', '/', ' 2 ', ') ', ') '.
(4) Decision function, the decision should be in the stack, the stack operation, or stack discard.
(5) Main function, traverse calculation list, calculate the final result.
32 Number Arithmetic function
Passes in two numbers, an operator, and returns the corresponding result, depending on the operator. That is, calculate subtraction:
def calculate (N1, N2, operator): "' :p Aram n1:float :p Aram N2:float :p aram Operator: +-*/ : retur N:float " result = 0 if operator = =" + ": result = n1 + N2 If operator ="-": result = N1-n2 if operator = = "*": result = N1 * N2 if operator = = "/": result = n1/n2
4 whether it is an operator or a number
You may think of IsDigit () to judge numbers, but this function cannot judge decimals and negative numbers. So, we write our own function to determine whether it is an operator:
# Determine if it is an operator, if it is returned Truedef Is_operator (e): ' :p Aram e:str : Return:bool ' ' opers = [' + ', '-', ' * ', '/', ' (', ') '] return True if E in opers else False
5 formatting the calculation as a list
This step needs to be handled by distinguishing between the '-'-' represents a negative or minus. For details, see the following example, the annotations are clear:
# deal with the calculation to the list, solve the question of whether the bar is negative or minus sign def formula_format (formula): # Remove the space in the calculation formula = Re.sub (",", Formula) # to ' The horizontal bar number ' splits, where the regular expression: (\-\d+\.*\d*) in parentheses: # \-indicates the match of the beginning of the bar, \d+ indicates a matching number 1 or more times; \. Indicates a matching decimal point 0 or 1 times; \d* indicates a match number 1 or more times. formula_list = [I for I in Re.split (' (\-\d+\.? \d*) ', formula) if I] # final calculation list Final_formula = [] for item in formula_list: # The first is a number preceded by a bar (including decimals) Final_ Formula Machine first is a negative number, the bar is not a minus if Len (final_formula) = = 0 and Re.search (' ^\-\d+\. \d*$ ', item): final_formula.append (item) continue If Len (Final_formula) > 0: # if Final_ Formal the last element is the operator [' + ', '-', ' * ', '/', ' ('), then the horizontal bar number is not a negative if Re.search (' [\+\-\*\/\ (]$ ', final_formula[-1]): Final_formula.append (item) continue # separated by operator open item_split = [I for I in Re.split (' ([\+\-\*\/\ (\)]) ', Item) If I] final_formula + = Item_split return Final_formula
6 decision Stack or stack
This function is more difficult and more abstract. Compare two consecutive operators to determine whether to stack or reload:
def decision (Tail_op, Now_op): ' :p Aram Tail_op: The last operator of the operator stack :p Aram Now_op: The current operator taken out of the calculation list : return : 1 for the stack operation, 0 for the last element of the bomb operator stack, 1 for the Stack " # to define 4 operator levels rate1 = [' + ', '-'] rate2 = [' * ', '/'] rate3 = [' (' ] rate4 = [') '] if tail_op in rate1: if now_op in Rate2 or Now_op in Rate3: # Indicates a different priority for two consecutive operations, need to stack Return-1 Else: return 1 elif tail_op in Rate2: if now_op in Rate3: return-1 Else: Return 1 elif tail_op in rate3: if now_op in rate4: return 0 # (encounter) need to eject (, discard) else: Retu Rn-1 # As long as the top element of the stack is (, the current element is not) should be in the stack. Else: return-1
7 Main function
The main function is responsible for traversing the characters in the calculation list, deciding to enter the number stack or operator stack or the stack operation.
def final_calc (formula_list): Num_stack = [] # number Stack op_stack = [] # operator Stack for E in formula_list: operator = Is_operator (e) If not operator: # presses into the number stack # string converted to a character count Num_stack.append ( Float (e)) Else: # If it is an operator while True: # If the operator stack equals 0 unconditionally into the stack if Len (op _stack) = = 0:op_stack.append (e) Break # decision function to make a decision Tag = Decision (Op_stack[-1], e) if tag = =-1: # 1 Press into the symbol stack to enter the next loop o P_stack.append (e) break elif tag = = 0: # If it is 0 pop-up inside the stack, go to the next loop Op_stack.pop () break elif Tag = = 1: # If it is 1 pop-up inside the stack element, pop the number element. op = Op_stack.pop () num2 = Num_stack.pop () NUM1 = Num_stack.pop () # Perform calculations # After calculation is pressed into the digital stack num_stack.append (Calculate (NUM1, num2, op)) # processing cycle after the end of the digital stack and the operator stack may also have The case of the element while Len (op_stack)! = 0:op = Op_stack.pop () num2 = Num_stack.pop () NUM1 = Num_stack.pop () Num_stack.append (Calculate (NUM1, num2, op)) return Num_stack, Op_stack
8 Ultimate Code and testing
Import redef Calculate (n1, N2, operator): "':p Aram n1:float:p Aram N2:float:p aram Operator: +-*/: R Eturn:float "result = 0 if operator = =" + ": result = n1 + N2 If operator ="-": result = N1 -n2 if operator = = "*": result = N1 * n2 If operator = "/": result = n1/n2 return result# determine if Is the operator, if it is returned Truedef Is_operator (e): ':p Aram e:str:return:bool ' ' opers = [' + ', '-', ' * ', '/', ' (', ') ' ] Return True if E in opers else false# to solve the problem of a negative or minus sign in the bar def Formula_format (formula): # Remove the space in the equation formula = Re.sub (', ', ', Formula) # is split with a ' cross-bar number ', where the regular expression: (\-\d+\.*\d*) in parentheses: # \-represents the beginning of the match bar, and \d+ indicates a matching number 1 or more times; \. Indicates a matching decimal point 0 or 1 times; \d* indicates a match number 1 or more times. Formula_list = [I for I in Re.split (' (\-\d+\.? \d*) ', formula) if I] # final calculation list final_formula = [] for item in Formula_list: # The first is a number preceded by a bar (including decimals) final_for Mula. Machine first is a negative number, the bar is not a minus if Len (final_formula) = = 0 and Re.search (' ^\-\d+\. \d*$', item): Final_formula.append (item) Continue if Len (Final_formula) > 0: # if fi Nal_formal the last element is the operator [' + ', '-', ' * ', '/', ' ('), then the horizontal bar number is not a negative if re.search (' [\+\-\*\/\ (]$ ', final_formula[-1]): Final_formula.append (item) Continue # Separated by operator open Item_split = [I for I in Re.split (' ([\+\-\*\/\ (\)]) ', item) if I] final_formula + = Item_split return final_formuladef decision (TAIL_OP, NOW_OP): "':p Aram Tail_op: The last operator of the operator stack:p Aram Now_op: The current operator taken out of the calculation list: Return:1 for the stack operation, 0 for the last element of the stack operator, 1 for the stack" # define 4 operator levels rate1 = [' + ', '-'] rate2 = [' * ', '/'] rate3 = [' ('] rate4 = [') '] if tail_op in rate1: If now_op in Rate2 or Now_op in Rate3: # indicates a different priority for two consecutive operations, need to be in the stack return-1 Else:ret Urn 1 elif tail_op in Rate2:if now_op in rate3:return-1 else:return 1 elif t Ail_op in Rate3:If now_op in Rate4:return 0 # (encounter) need to pop up (, throw away) Else:return-1 # as long as the top element of the stack is (, the current element is not) should be in the stack. Else:return-1def Final_calc (formula_list): Num_stack = [] # number Stack op_stack = [] # operator Stack fo R e in formula_list:operator = Is_operator (e) If not operator: # press the number stack # string to a character count Num_stack.append (float (e)) Else: # If it is an operator while True: # If the operator stack equals 0 unconditional entry Stack if len (op_stack) = = 0:op_stack.append (e) Break # de Cision function make a decision tag = Decision (Op_stack[-1], e) if tag = =-1: # If yes-1 press into the symbol stack Next cycle Op_stack.append (e) break elif tag = = 0: # if is 0 popup inside the stack element, into the next loop Op_stack.pop () break elif Tag = = 1: # if it is 1 pop-up inside the stack element, pop the number element. op = Op_stack.pop () num2 = Num_stack.pop () NUM1 = Num_stack.pop () # Perform calculation # calculation after pressing into digital stack num_stack.append (Calculate (NUM1, num2, op)) # processing cycle After the end of the digital stack and the operator stack may have elements in the case while Len (op_stack)! = 0:op = Op_stack.pop () num2 = Num_stack.pop () NUM1 = Num_stack.pop () num_stack.append (Calculate (NUM1, num2, op)) return num_stack, op_stackif __name__ = = ' __main__ ': Formula = "1-2 * ((60-30 + ( -40/5) * (9-2*5/3 + 7/3*99/4*2998 +10 * 568/14))-( -4*3)/(16-3*2))" PRI NT ("equation:", formula) Formula_list = Formula_format (Formula) result, _ = Final_calc (formula_list) print ("Calculated result:", res Ult[0]) # equation: 1-2 * ((60-30 + ( -40/5) * (9-2*5/3 + 7/3*99/4*2998 +10 * 568/14))-( -4*3)/(16-3*2)) # calculation result: 2776672.6952 380957
Let's look at the results of Google operations:
The explanation is that we are right, we may as well test some formulas to see.
The end of this article.
Python Implementation Calculator