该计算器程序使用中缀表达式转后缀表达式的策略,并通过栈实现计算;其核心步骤为:1.定义运算符优先级函数precedence;2.实现中缀转后缀函数infixtopostfix,利用栈处理运算符并生成后缀队列;3.实现后缀表达式求值函数evaluatepostfix,用栈存储操作数并根据运算符执行计算;4.主函数main接收输入并调用上述函数,同时捕获和处理异常;5.空格处理依赖stringstream自动忽略并分割token;6.扩展新运算符需在precedence添加优先级并在evaluatepostfix的switch中添加逻辑;7.支持函数调用需增加函数注册表、修改infixtopostfix识别函数名、并在evaluatepostfix中调用对应函数。
用C++实现一个简单的计算器,核心在于解析用户输入的字符串,识别数字和运算符,然后按照运算优先级进行计算。 这听起来简单,但细节很多,比如错误处理、不同数据类型的兼容性等等。
解决方案
首先,我们需要一个主函数来接收用户的输入,并调用相应的函数进行处理。
立即学习“C++免费学习笔记(深入)”;
#include <iostream> #include <string> #include <stack> #include <queue> #include <sstream> #include <cctype> #include <stdexcept> #include <cmath> using namespace std; // 定义运算符优先级 int precedence(char op) { if (op == '+' || op == '-') return 1; if (op == '*' || op == '/') return 2; if (op == '^') // 指数运算 return 3; return 0; } // 中缀表达式转后缀表达式 queue<string> infixToPostfix(const string& expression) { queue<string> postfixQueue; stack<char> operatorStack; stringstream ss(expression); string token; while (ss >> token) { if (isdigit(token[0]) || (token.size() > 1 && isdigit(token[1]) && (token[0] == '-' || token[0] == '+'))) { // 数字直接入队列 (处理负数) postfixQueue.push(token); } else if (token == "(") { operatorStack.push('('); } else if (token == ")") { while (!operatorStack.empty() && operatorStack.top() != '(') { postfixQueue.push(string(1, operatorStack.top())); operatorStack.pop(); } if (!operatorStack.empty() && operatorStack.top() == '(') { operatorStack.pop(); // 弹出 '(' } else { throw invalid_argument("Mismatched parentheses"); // 括号不匹配 } } else { // 运算符处理 char op = token[0]; // 假设token长度为1 while (!operatorStack.empty() && precedence(op) <= precedence(operatorStack.top())) { postfixQueue.push(string(1, operatorStack.top())); operatorStack.pop(); } operatorStack.push(op); } } // 将剩余运算符弹出 while (!operatorStack.empty()) { if (operatorStack.top() == '(') { throw invalid_argument("Mismatched parentheses"); // 括号不匹配 } postfixQueue.push(string(1, operatorStack.top())); operatorStack.pop(); } return postfixQueue; } // 计算后缀表达式 double evaluatePostfix(queue<string> postfixQueue) { stack<double> operandStack; while (!postfixQueue.empty()) { string token = postfixQueue.front(); postfixQueue.pop(); if (isdigit(token[0]) || (token.size() > 1 && isdigit(token[1]) && (token[0] == '-' || token[0] == '+'))) { // 数字入栈 operandStack.push(stod(token)); } else { // 运算符处理 if (operandStack.size() < 2) { throw invalid_argument("Invalid expression"); // 缺少操作数 } double operand2 = operandStack.top(); operandStack.pop(); double operand1 = operandStack.top(); operandStack.pop(); char op = token[0]; switch (op) { case '+': operandStack.push(operand1 + operand2); break; case '-': operandStack.push(operand1 - operand2); break; case '*': operandStack.push(operand1 * operand2); break; case '/': if (operand2 == 0) { throw invalid_argument("Division by zero"); // 除数为零 } operandStack.push(operand1 / operand2); break; case '^': operandStack.push(pow(operand1, operand2)); break; default: throw invalid_argument("Invalid operator"); // 无效运算符 } } } if (operandStack.size() != 1) { throw invalid_argument("Invalid expression"); // 表达式无效 } return operandStack.top(); } int main() { string expression; cout << "请输入算术表达式(用空格分隔数字和运算符,支持+ - * / ^):" << endl; getline(cin, expression); try { queue<string> postfixQueue = infixToPostfix(expression); double result = evaluatePostfix(postfixQueue); cout << "结果: " << result << endl; } catch (const exception& e) { cerr << "错误: " << e.what() << endl; return 1; } return 0; }
这段代码首先定义了运算符的优先级,然后实现了一个中缀表达式转后缀表达式的函数
infixToPostfix
,最后实现了一个计算后缀表达式的函数
evaluatePostfix
。 主函数负责接收用户输入,调用这两个函数,并输出结果。当然,这段代码还包含了一些错误处理,比如括号不匹配、除数为零等等。
如何处理用户输入中的空格?
在上面的代码中,我们使用了
stringstream
来处理用户输入。
stringstream
会自动忽略空格,并将字符串分割成一个个的 token。 因此,用户可以在输入表达式时随意添加空格,程序仍然可以正确解析。
如何扩展计算器的功能,比如添加更多运算符?
要添加更多运算符,首先需要在
precedence
函数中定义新运算符的优先级。 然后,在
evaluatePostfix
函数的
switch
语句中添加新运算符的处理逻辑。 例如,要添加求模运算符
%
,可以这样修改代码:
// 在 precedence 函数中添加 % 的优先级 int precedence(char op) { if (op == '+' || op == '-') return 1; if (op == '*' || op == '/') return 2; if (op == '%') // 求模运算 return 2; if (op == '^') // 指数运算 return 3; return 0; } // 在 evaluatePostfix 函数的 switch 语句中添加 % 的处理逻辑 switch (op) { case '+': operandStack.push(operand1 + operand2); break; case '-': operandStack.push(operand1 - operand2); break; case '*': operandStack.push(operand1 * operand2); break; case '/': if (operand2 == 0) { throw invalid_argument("Division by zero"); // 除数为零 } operandStack.push(operand1 / operand2); break; case '%': if (operand2 == 0) { throw invalid_argument("Division by zero"); // 除数为零 } operandStack.push(fmod(operand1, operand2)); // 使用 fmod 处理浮点数求模 break; case '^': operandStack.push(pow(operand1, operand2)); break; default: throw invalid_argument("Invalid operator"); // 无效运算符 }
注意,求模运算需要使用
fmod
函数来处理浮点数。
如何处理更复杂的表达式,比如包含函数调用?
处理包含函数调用的表达式会更复杂。 首先,需要在
infixToPostfix
函数中识别函数名和参数。 然后,在
evaluatePostfix
函数中调用相应的函数,并将结果压入栈中。 这需要一个函数注册表,将函数名和函数指针关联起来。 例如:
#include <map> // 定义函数指针类型 typedef double (*FunctionPtr)(double); // 函数注册表 map<string, FunctionPtr> functionTable; // 注册函数 void registerFunction(const string& name, FunctionPtr function) { functionTable[name] = function; } // 实现一个示例函数 double sine(double x) { return sin(x); } int main() { // 注册 sine 函数 registerFunction("sin", sine); // ... (修改 infixToPostfix 和 evaluatePostfix 函数) return 0; }
然后,需要修改
infixToPostfix
函数,识别函数调用,并将函数名和参数转换为后缀表达式。 例如,将
sin(3.14)
转换为
3.14 sin
。 最后,修改
evaluatePostfix
函数,当遇到函数名时,从函数注册表中查找函数指针,并调用该函数。 这部分代码比较复杂,需要仔细设计和实现。
评论(已关闭)
评论已关闭