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

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

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

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

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

         具体程序如下:

// 通过前缀表达式计算中缀表达式,词法分析

#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:前缀的计算

 

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

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

 

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

你可能感兴趣的:(表达式)