通过前缀表达式计算中缀表达式
通过前缀表达式计算中缀表达式
之前我们有讨论《中缀表达式转换为前缀表达式》和《前缀表达式的计算》,通过这两部分,我们可以先将一个中缀表达式转换为前缀表达式,进而利用前缀表达式的计算方法得到前缀表达式的值,也就是中缀表达式的值。
在《中缀表达式转换为前缀表达式》中,我们对中缀表达式的输入是用空白符间隔的,这样对于用户的输入有很大的限制性。我们最理想的情况是用户既可以输入空白符也可以不输入,即根据个人喜好自由输入。典型的方法就是对输入的表示中缀表达式的字符串进行词法分析,解析出对应的中缀表达式,进而进行转换操作和计算。关于中缀表达式的词法分析,我们可以参考《四则运算的词法分析》。
本文我们是将上面提到的三部分进行整合,用户输入一个字符串,我们首先通过词法分析解析出对应的中缀表达式,然后将中缀表达式转换为前缀表达式,进而对前缀表达式进行计算得到输入的中缀表达式的值。
具体程序如下:
// 通过前缀表达式计算中缀表达式,词法分析 #include <iostream> #include <sstream> #include <vector> #include <string> #include <map> #include <stack> using namespace std; // 加入词法分析 BEGIN struct TI { string token; int id; }; bool is_blank(char ch) { return ch == ' ' || ch == ' '; } void get_exp(string& exp) { getline(cin, exp); } // token及其编码 void init_keys(map<string, int>& keys) { keys.clear(); keys["+"] = 1; keys["-"] = 2; keys["*"] = 3; keys["/"] = 4; keys["("] = 5; keys[")"] = 6; keys["__NUM__"] = 7; } void lex(const string& exp, vector<TI>& to_id, const map<string, int>& keys) { to_id.clear(); char ch; for (string::size_type pos = 0; pos < exp.size(); /* ++pos */) { TI ti; ch = exp[pos]; if (is_blank(ch)) { ++pos; continue; } if (ch >= '0' && ch <= '9' || ch == '.') { ti.token += ch; ++pos; if (pos >= exp.size()) { ti.id = keys.size(); to_id.push_back(ti); return; } ch = exp[pos]; while (ch >= '0' && ch <= '9' || ch == '.') { ti.token += ch; ++pos; if (pos >= exp.size()) { ti.id = keys.size(); to_id.push_back(ti); return; } ch = exp[pos]; } ti.id = keys.size(); to_id.push_back(ti); } else { map<string, int>::const_iterator cit; switch (ch) { case '+': case '-': case '*': case '/': case '(': case ')': ti.token += ch; cit = keys.find(ti.token); if (cit == keys.end()) { cout << "test" << endl; } ti.id = cit->second; to_id.push_back(ti); ++pos; break; default: // ti.token += string("Unknown:") + ch; ti.token += string("未知字符:") + ch; ti.id = -1; to_id.push_back(ti); ++pos; break; } } } } // 基于词法分析读取中缀表达式 void get_infix_lex(vector<string>&inf, const map<string, int>& keys) { inf.clear(); string line; getline(cin, line); vector<TI> to_id; lex(line, to_id, keys); for (vector<TI>::size_type i = 0; i != to_id.size(); ++i) { inf.push_back(to_id[i].token); } } // 加入词法分析 END // 中缀转前缀 BEGIN // 通过空白符进行间隔 void GetInfix(vector<string>& infix) { infix.clear(); string line; getline(cin, line); istringstream sin(line); string tmp; while (sin >> tmp) { infix.push_back(tmp); } } // 初始化操作符 void InitOperators(map<string, int>& opers) { opers.clear(); opers["("] = 100; opers[")"] = 900; opers["+"] = 100; opers["-"] = 100; opers["*"] = 200; opers["/"] = 200; } bool IsOperator(const string& op, const map<string, int>& opers) { auto cit = opers.find(op); if (cit != opers.end()) { return true; } else { return false; } } void InfixToPrefix(const vector<string>& infix, vector<string>& prefix, map<string, int>& opers) { prefix.clear(); stack<string> stk; // 操作符栈 for (int i = infix.size() - 1; i >= 0; --i) // 从右到左扫描 { if (!IsOperator(infix[i], opers)) // 如果是操作数 { prefix.push_back(infix[i]); } else // 如果是操作符 { if (infix[i] == ")") // 如果是右括号,则直接入栈 { stk.push(infix[i]); } else if (infix[i] == "(") // 如果是左括号 { // 依次弹出栈中的操作符,直至遇到右括号 while (!stk.empty()) { if (stk.top() == ")") { stk.pop(); break; } else { prefix.push_back(stk.top()); stk.pop(); } } } else // 如果是其他操作符 { while (!stk.empty() && stk.top() != ")" && opers[stk.top()] > opers[infix[i]]) // 栈顶操作符优先级大于当前操作符优先级 { prefix.push_back(stk.top()); stk.pop(); } // 将当前操作符入栈 stk.push(infix[i]); } } } // 检测操作符栈是否为空 while (!stk.empty()) { prefix.push_back(stk.top()); stk.pop(); } // 将prefix翻转 reverse(prefix.begin(), prefix.end()); return; } void Display(const vector<string>& fix) { for (auto i = 0; i != fix.size(); ++i) { cout << fix[i] << ' '; } cout << endl; } // 中缀转前缀 END // 前缀的计算 BEGIN // 读取前缀表达式 void GetPrefix(vector<string>& prefix) { prefix.clear(); string line, tmp; getline(cin, line); istringstream sin(line); while (sin >> tmp) { prefix.push_back(tmp); } } // 将IsOperator重载,单参数 bool IsOperator(const string& op) { return op == "+" || op == "-" || op == "*" || op == "/"; } double CalPrefix(const vector<string>& prefix) { double ret = 0.0; stack<double> opeStk; for (int i = prefix.size() - 1; i >= 0; --i) { if (!IsOperator(prefix[i])) { opeStk.push((double)atof(prefix[i].c_str())); } else { double a = opeStk.top(); opeStk.pop(); double b = opeStk.top(); opeStk.pop(); double c = 0.0; switch (prefix[i][0]) { case '+': c = a + b; opeStk.push(c); break; case '-': c = a - b; opeStk.push(c); break; case '*': c = a * b; opeStk.push(c); break; case '/': c = a / b; opeStk.push(c); break; default: break; } } } if (opeStk.size() == 1) { return opeStk.top(); } else { return -1000000000.0; } } // 前缀的计算 END int main() { map<string, int> keys; // 用于词法分析 init_keys(keys); map<string, int> opers; // 操作符及其优先级 InitOperators(opers); vector<string> infix; vector<string> prefix; while (true) { get_infix_lex(infix, keys); InfixToPrefix(infix, prefix, opers); Display(prefix); cout << CalPrefix(prefix) << endl; cout << endl; } return 0; }
在程序中几个重要的函数:
lex:对字符串进行词法分析
InfixToPrefix:中缀转后缀
CalPrefix:前缀的计算
目前关于四则运算方面的讨论系列,我们已经讨论了以下议题:
- 后缀表达式的计算
- 中缀转后缀
- 通过后缀表达式计算中缀表达式
- 前缀表达式的计算
- 中缀转前缀
- 通过前缀表达式计算中缀表达式
- 对输入的字符串进行空白符的预处理
- 对输入的字符串进行词法分析得到相应的中缀表达式
- 对于代数表达式的词法分析、转换、计算
- 后缀表达式转换中缀表达式的三个层次的讨论
- 检测中缀表达式的合法性,转换、计算两个过程的错误处理机制
对于四则运算方面的讨论我们暂时告一段落,以后会根据想法再有所补充。
(完)
文档信息
·版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
·博客地址:http://www.cnblogs.com/unixfy
·博客作者:unixfy
·作者邮箱:goonyangxiaofang(AT)163.com
·如果你觉得本博文的内容对你有价值,欢迎对博主 小额赞助支持