程序最美(寻路)

你还在坚持练习你的技术吗?运动员天天训练,音乐家也会演练更难的曲章。你呢?

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

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

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

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

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

         具体程序如下:

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

 

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

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

 

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

posted on 2013-09-30 17:15  unixfy  阅读(758)  评论(0编辑  收藏  举报

导航