Simple calculator C code implementation ideas and code

insert image description here
The key to this problem is that the calculation order of infix expressions is related to operators, not simply from left to right. The calculation priority of suffix expressions is exactly from left to right, and the logic of converting infix to suffix can be applied to the calculation of infix expressions.

Infix to postfix expression algorithm (computer calculation):

  1. Initialize a stack: Since the operator of the operation order cannot be determined for the time being, each element needs to be processed from left to right until the end;
  2. Encountered operands: directly add the suffix expression;
  3. Encountered delimiter: When encountering "(" directly pushed onto the stack, encountering ")", the operator in the stack will be popped up one by one and the suffix expression will be added until "(" is popped up. Note that "(" will not add the suffix expression;
  4. Encountered an operator; pop all operators in the stack whose priority is higher than or equal to the current operator in turn, and add a suffix expression, if it encounters "(" or the stack is empty, stop. Then put the current operator on the stack.

insert image description here

The algorithm for machine-implemented calculation of infix expressions is as follows:

  1. Initialize two stacks, operand stack and operator stack;
  2. If the operand is scanned, it is pushed into the operand stack;
  3. If the operator or delimiter is scanned, it will be pushed into the operator stack according to the same logic as "infix to suffix" (whenever an operator is popped, it is necessary to pop the top elements of two operation stacks and perform the corresponding operation , and the result of the operation is pushed back to the operand stack).

insert image description here

After understanding the above two algorithms, they can be applied to this question.

code analysis

According to the title, there are three types of characters that will appear in the input expression: numbers, spaces, and operators.
When a space is encountered, it means that the front is either a number or an operator;
when a complete data is encountered, it is pushed onto the stack;
when an operator is encountered, the current operator needs to be compared with the operation The priority of the top element of the character stack.

After the analysis is completed, you can start writing the code. The code is introduced in sections below:

Character array buf: used to save the input expression, C-style string;
string num: used to save the number, the number may have more than one digit, so it is saved with a string;
string expr: used to save the string array Convert to a C++-style string;
operand stack NumStack: save the operand;
operator stack SignStack: save the operator;
map type priority: set the priority for the operator, and then determine how to operate according to the priority in the algorithm. Add a terminator to the operator, and set the priority of the terminator to be the lowest, so as to ensure that all previous expressions are calculated after the terminator is encountered.

char buf[300];
int i;
double add_num, sum = 0;
string num = "", expr;
stack<double> NumStack;
stack<char> SignStack;
map<char, int> priority = {
   
   {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}, {'$', 0}};

Use the function fgets()function to store all the input expressions in the char array, but fgets()the function will also store the end of the \nline in the array;
convert the C-style char array into a C++-style string type, save it in the expr variable, and then Delete the newline character at the end of the string;
if there is only expr left "0", it means that there is no other expression input, and exit the program;
finally add a terminator at the end of the expr variable "$".

while(fgets(buf, 300, stdin) != NULL){
	expr = buf;
	expr.pop_back();
	if(expr == "0"){
		break;
	}
	expr.push_back('$');  // add a terminator

Traverse the characters in expr, mainly to deal with symbols that may be numbers, spaces, operators, etc. When: the
character is a number: push the current number to the end of num, because the number may be more than one, so only after encountering a space It can be judged that a complete number has been obtained;
the character is a space: when a space is encountered, the result just obtained may be an operand or an operator, and whether it is an operand can be judged according to whether num is, if it ""is Operand, push into NumStack, and convert num to double type number, stod()this function can be directly realized by using function;
when the character is other types: it can be addition, subtraction, multiplication, division and terminator, if it is terminator, it means that you got it earlier An operand, so first push this operand into the NumStack stack, and then process the symbol. Only when the operator stack is empty or the priority of the current operator is higher than the priority of the operator at the top of the operator stack, the operator is pushed onto the operator stack; if the operator stack is not empty and the priority of the current operator is less than or equal to the priority of the operator at the top of the operator stack, the top elements of the two operation stacks are popped and the corresponding operations are performed, and the operation results are pushed back to the operand stack.

// 如果为数字
if(expr[i] >= '0' && expr[i] <= '9'){
	num.push_back(expr[i]);
}else if(expr[i] == ' '){
	if(num != ""){
		NumStack.push(stod(num));
		num = "";
	}else{
		continue;
	}
}else{
	// add_num = NumStack.top();
	if(expr[i] == '$'){
		NumStack.push(stod(num));
		num = "";
	}
	while(!SignStack.empty()){
		if(priority[expr[i]] <= priority[SignStack.top()]){
			add_num = NumStack.top();
			NumStack.pop();
			switch (SignStack.top())
			{
			case '+':
				sum = NumStack.top() + add_num;
				break;
			case '-':
				sum = NumStack.top() - add_num;
				break;
			case '*':
				sum = NumStack.top() * add_num;
				break;
			case '/':
				sum = NumStack.top() / add_num;
				break;
			default:
				;
				break;
			}
			NumStack.pop();
			NumStack.push(sum);
			SignStack.pop();
		}else{
			break;
		}
	}
	SignStack.push(expr[i]);
}

After a round of looping, the top element of the operand stack is the calculated value of the expression, which can be output. Finally, the top element of the NumStack (expression calculation result) and the top element of the SignStack (terminator) need to be popped out. , the purpose is to clear the operand stack and operator stack, in order to facilitate the traversal calculation of the next line of expressions.

printf("%.02f\n", NumStack.top());
NumStack.pop();
SignStack.pop();

Full code:

#include <cstdio>
#include <string>
#include <stack>
#include <map>
using namespace std;
int main(){
    char buf[300];
    int i;
    double add_num, sum = 0;
    string num = "", expr;
    stack<double> NumStack;
    stack<char> SignStack;
    map<char, int> priority = {
   
   {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}, {'$', 0}};
    while(fgets(buf, 300, stdin) != NULL){
        expr = buf;
        expr.pop_back();
        if(expr == "0"){
            break;
        }
        expr.push_back('$');  // add a terminator
        // 遍历整行   处理数字,处理空格,处理符号
        for(i=0; i<expr.size(); i++){
            // 如果为数字
            if(expr[i] >= '0' && expr[i] <= '9'){
                num.push_back(expr[i]);
            }else if(expr[i] == ' '){
                if(num != ""){
                    NumStack.push(stod(num));
                    num = "";
                }else{
                    continue;
                }
            }else{
                // add_num = NumStack.top();
                if(expr[i] == '$'){
                    NumStack.push(stod(num));
                    num = "";
                }
                while(!SignStack.empty()){
                    if(priority[expr[i]] <= priority[SignStack.top()]){
                        add_num = NumStack.top();
                        NumStack.pop();
                        switch (SignStack.top())
                        {
                        case '+':
                            sum = NumStack.top() + add_num;
                            break;
                        case '-':
                            sum = NumStack.top() - add_num;
                            break;
                        case '*':
                            sum = NumStack.top() * add_num;
                            break;
                        case '/':
                            sum = NumStack.top() / add_num;
                            break;
                        default:
                            ;
                            break;
                        }
                        NumStack.pop();
                        NumStack.push(sum);
                        SignStack.pop();
                    }else{
                        break;
                    }
                }
                SignStack.push(expr[i]);
            }
        }
        printf("%.02f\n", NumStack.top());
        NumStack.pop();
        SignStack.pop();
    }
    return 0;
}

Note: When inputting, you need to read one line at a time. When I wrote it for the first time, I read it by character, and finally when I submitted the result, it showed that the operation timed out. However, using the fgets() function to read a line and store it in a char array, and then transfer it to a variable of type string and then traverse will save a lot of time.

The code record of running timeout for the first time (low efficiency, the algorithm mechanism is the same as above):

#include <cstdio>
#include <string>
#include <stack>
#include <map>
using namespace std;
 
double Calculate(char symbol, double x, double y);
 
int main(){
    double sum=0, add_num, tmp;
    char symbol;
    stack<double> num;
    stack<char> signal;
    map<char, int> priority = {
   
   {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}, {'\n', 0}};
    do{
        if(!num.empty()){
            num.pop();
        }
        if(!signal.empty()){
            signal.pop();
        }
        scanf("%lf", &add_num);
        fgets(&symbol, 2, stdin);
        if(symbol == '\n'){
            continue;
        }else{
            fgets(&symbol, 2, stdin);
            signal.push(symbol);
            num.push(add_num);
            /
            do{
                scanf("%lf", &add_num);
                fgets(&symbol, 2, stdin);
                if(symbol != '\n'){
                    fgets(&symbol, 2, stdin);
                }
                num.push(add_num);
                // 循环
                do{
                    if(priority[symbol] <= priority[signal.top()]){
                        tmp = num.top();
                        num.pop();
                        sum = Calculate(signal.top(), num.top(), tmp);
                        num.pop();
                        signal.pop();
                        num.push(sum);
                    }else{
                        break;
                    }
                }while ((!signal.empty()));
                signal.push(symbol);
                 
                 
            }while(symbol != '\n');
            /
            printf("%.02f\n", num.top());
        }
    }while(add_num != 0 || symbol != '\n');
    return 0;
}
 
double Calculate(char symbol, double x, double y){
    switch (symbol)
    {
    case '+':
        return x + y;
        break;
    case '-':
        return x - y;
        break;
    case '*':
        return x * y;
        break;
    default:
        return x / y;
        break;
    }
}

Guess you like

Origin blog.csdn.net/weixin_42992706/article/details/129112489