zoukankan      html  css  js  c++  java
  • 通过前缀表达式计算中缀表达式

    通过前缀表达式计算中缀表达式

             之前我们有讨论《中缀表达式转换为前缀表达式》和《前缀表达式的计算》,通过这两部分,我们可以先将一个中缀表达式转换为前缀表达式,进而利用前缀表达式的计算方法得到前缀表达式的值,也就是中缀表达式的值。

             在《中缀表达式转换为前缀表达式》中,我们对中缀表达式的输入是用空白符间隔的,这样对于用户的输入有很大的限制性。我们最理想的情况是用户既可以输入空白符也可以不输入,即根据个人喜好自由输入。典型的方法就是对输入的表示中缀表达式的字符串进行词法分析,解析出对应的中缀表达式,进而进行转换操作和计算。关于中缀表达式的词法分析,我们可以参考《四则运算的词法分析》。

             本文我们是将上面提到的三部分进行整合,用户输入一个字符串,我们首先通过词法分析解析出对应的中缀表达式,然后将中缀表达式转换为前缀表达式,进而对前缀表达式进行计算得到输入的中缀表达式的值。

             具体程序如下:

    // 通过前缀表达式计算中缀表达式,词法分析
    #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:前缀的计算

             目前关于四则运算方面的讨论系列,我们已经讨论了以下议题:

    •          后缀表达式的计算
    •          中缀转后缀
    •          通过后缀表达式计算中缀表达式
    •          前缀表达式的计算
    •          中缀转前缀
    •          通过前缀表达式计算中缀表达式
    •          对输入的字符串进行空白符的预处理
    •          对输入的字符串进行词法分析得到相应的中缀表达式
    •          对于代数表达式的词法分析、转换、计算
    •          后缀表达式转换中缀表达式的三个层次的讨论
    •          检测中缀表达式的合法性,转换、计算两个过程的错误处理机制

             对于四则运算方面的讨论我们暂时告一段落,以后会根据想法再有所补充。

  • 相关阅读:
    sublime text
    php 实例说明 socket通信机制
    nusaop 关于webService
    vim操作集合
    gitHud设置公钥
    redis在window安装并启动
    百度云api 添加标注
    微信小程序bindTap获取对应值
    Java导出excel表
    linux 常用命令
  • 原文地址:https://www.cnblogs.com/unixfy/p/3347599.html
Copyright © 2011-2022 走看看