检测中缀表达式的合法性

检测中缀表达式的合法性

         前面我们对《中缀表达式的计算》以及《检测后缀表达式的合法性》进行了讨论,这里我们借助于之前对于两者的讨论,来对中缀表达式的合法性进行检测。如果中缀表达式是合法的,则返回该表达式的值;如果是非法的,则提示其非法。

         我们对中缀表达式的合法性检测主要分为两个阶段:

         1)中缀表达式转换为后缀表达式过程中是否存在错误;

         2)后缀表达式的计算过程中是否存在错误

         其中,第2阶段我们在《中缀表达式的计算》中已经做过讨论,这里重点在于第1阶段。

         在第1阶段中主要涉及以下几个合法性检测:

         <1>.操作数是否合法;

         <2>.左括号和右括号是否匹配;

         一、操作数是否合法

         合法的操作数首个字符可以是“+”、“-”,也可以是“0”-“9”。后面的字符最多只包含一个“.”字符,其余字符都是“0”-“9”。

         具体的实现相见程序。

         二、左括号和右括号是否匹配

         实现左括号和右括号是否匹配有两种方式:

         <1>.设置一个记录项,当遇到“(”时,自动加1,,当遇到“)”时,自动减1,每当减1后,检测该记录项是否大于等于0,如果大于则说明没问题,如果小于0,则说明从左到右“)”的数目大于”(”的数目,也就是说二者不匹配,这时终止返回。

         如果在扫描的过程中没有出现记录项小于0,当扫描完中缀表达式后,检测记录项是否大于0,如果大于则说明中缀表达式中“(”的数量大于”)”,说明左括号和右括号不匹配,终止返回。

         <2>.第二种实现方式是:对“(”的情况不作处理,直接压栈,当遇到”)”时,设置一个标示符,用来记录弹栈循环中是否遇到了“(”,当弹栈循环结束后,检测该标示符,如果没有出现“(”,则说明左括号与右括号不匹配,右括号数量大于左括号数量,终止返回。

         当扫描完中缀表达式后,在对操作符栈进行弹栈过程中检测栈中是否还有“(”,如果有则说明左括号与右括号不匹配,左括号数量大于右括号数量,终止返回。

         在我们的实现中,我们采用第一种方式,因为第一种方式只计算了左括号和右括号出现的情况;而第二种情况为了检测左括号,必须计算栈中其他操作符的情况。所以,第一种方式的效率更高。

         此外,还有N目操作符与其操作数的位置关系检测、空白中缀表达式的检测等。

         具体程序如下:

// 中缀表达式的合法性检测
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <stack>
#include <map>
using namespace std;

string& replace_all_distinct(string& str, const string& src, const string& des)
{
    for (string::size_type i = 0; i != string::npos; i += des.size())
    {
        i = str.find(src, i);
        if (i != string::npos)
        {
            str.replace(i, src.size(), des);
        }
        else
        {
            break;
        }
    }
    return str;
}

string& n_replace(string& str, const vector<string>& src, const vector<string>& des)
{
    assert(src.size() > 0 && src.size() == des.size());
    for (vector<string>::size_type i = 0; i != src.size(); ++i)
    {
        replace_all_distinct(str, src[i], des[i]);
    }
    return str;
}

void get_infix(vector<string>& inf, const vector<string>& src, const vector<string>& des)
{
    inf.clear();
    string line;
    getline(cin, line);
    
    n_replace(line, src, des);
    
    istringstream sin(line);
    string tmp;
    while (sin >> tmp)
    {
        inf.push_back(tmp);
    }
}

void show(const vector<string>& hs)
{
    for (vector<string>::size_type i = 0; i != hs.size(); ++i)
    {
        cout << hs[i] << ' ';
    }
    cout << endl;
}

void init_op(map<string, int>& ops)
{
    ops.clear();
    ops["+"] = 100;
    ops["-"] = 100;
    ops["*"] = 200;
    ops["/"] = 200;
    ops["("] = 1000;
    ops[")"] = 0;
}

bool is_operator(const string& hs, const map<string, int>& ops)
{
    map<string, int>::const_iterator cit = ops.find(hs);
    if (cit != ops.end())
    {
        return true;
    }
    else
    {
        return false;
    }
}

// 判断操作数是否合法
bool op_legal(const string& str, int& ill_id)
{
    assert(str.size() > 0);
    string::size_type i = 0;
    if (str[i] == '+' || str[i] == '-')
    {
        ++i;
        if (i == str.size())
        {
            ill_id = 3001;
            return false;
        }
    }
    int dot_num = 0;
    for (; i != str.size(); ++i)
    {
        if (isdigit(static_cast<int>(str[i])))
        {
            ;
        }
        else if (str[i] == '.')
        {
            ++dot_num;
        }
        else
        {
            ill_id = 3002;
            return false;
        }
    }
    if (dot_num > 1)
    {
        ill_id = 3003;
        return false;
    }
    return true;
}

void in2post(const vector<string>& inf, vector<string>& postf, map<string, int>& ops, bool& leg, int& ill_id)
{
    if (inf.size() == 0)
    {
        leg = false;
        ill_id = 6001;
        return;
    }
    
    postf.clear();
    stack<string> op_st;
    // 记录左括号和右括号之间的数量关系
    int brac = 0;
    int op_op = 0;
    
    for (vector<string>::size_type i = 0; i != inf.size(); ++i)
    {
        if (!is_operator(inf[i], ops))
        {
            // 判断是否是正确的操作数
            int tmp = 0;
            if (!op_legal(inf[i], tmp))
            {
                leg = false;
                ill_id = tmp;
                return;
            }
            ++op_op;
            if (op_op > 1)
            {
                leg = false;
                ill_id = 5001;
                return;
            }
            postf.push_back(inf[i]);
        }
        else
        {
            if (inf[i] == "(")
            {
                ++brac;
                op_st.push(inf[i]);
            }
            else if (inf[i] == ")")
            {
                --brac;
                if (brac < 0)
                {
                    leg = false;
                    ill_id = 4001;
                    return;
                }
                while (!op_st.empty())
                {
                    if (op_st.top() == "(")
                    {
                        op_st.pop();
                        // !勘误!
                        // 如果inf[i] == ")",当遇到"(",将"("弹栈后必须终止弹栈循环。
                        break;
                    }
                    else
                    {
                        postf.push_back(op_st.top());
                        op_st.pop();
                    }
                }
            }
            else // 若为其他运算符
            {
                --op_op;                
                if (op_op < 0)
                {
                    leg = false;
                    ill_id = 5002;
                    return;
                }
                
                if (op_st.empty()) // 若为空栈,则直接入栈
                {
                    op_st.push(inf[i]);
                }
                else
                {
                    if (ops[inf[i]] > ops[op_st.top()])
                    {
                        // 如果当前操作符优先级高于站定操作符优先级
                        // 则直接入栈
                        op_st.push(inf[i]);
                    }
                    else
                    {
                        // 否则弹出栈中优先级大于等于当前操作符优先级
                        // 的操作符,并最后将当前操作符压栈
                        while (!op_st.empty() && ops[op_st.top()] >= ops[inf[i]] && op_st.top() != "(")
                        {
                            /* 等价于 && op_st.top != "("
                            if (op_st.top() == "(")
                            {
                                // 如果当前栈顶操作符为 "("
                                // 则终止操作,继续保留 "(" 的栈顶位置
                                break;
                            }
                            */
                            postf.push_back(op_st.top());
                            op_st.pop();
                        }
                        op_st.push(inf[i]);
                    }
                }
            }
        }
    }
    if (brac > 0)
    {
        leg = false;
        ill_id = 4002;
        return;
    }
    if (op_op != 1)
    {
        leg = false;
        ill_id = 5003;
        return;
    }
    
    while (!op_st.empty())
    {
        postf.push_back(op_st.top());
        op_st.pop();
    }
    leg = true;
    return;
}

double cal_post(const vector<string>& postf, const map<string, int>& ops, bool& leg, int& ill_id)
{
    stack<double> or_st;
    double operand = 0.0, a = 0.0, b = 0.0, c = 0.0;
    for (vector<string>::size_type i = 0; i != postf.size(); ++i)
    {
        if (!is_operator(postf[i], ops))
        {
            operand = static_cast<double>(atof(postf[i].c_str()));
            or_st.push(operand);
        }
        else
        {
            switch (postf[i][0])
            {
            case '+':
                // 检测后缀表达式的合法性:操作数是否足够
                if (or_st.size() < 2)
                {
                    leg = false;
                    ill_id = 1001;
                    return -10000000000000.0;
                }
                b = or_st.top();
                or_st.pop();
                a = or_st.top();
                or_st.pop();
                c = a + b;
                or_st.push(c);
                break;
            case '-':
                // 检测后缀表达式的合法性:操作数是否足够
                if (or_st.size() < 2)
                {
                    leg = false;
                    ill_id = 1002;
                    return -10000000000000.0;
                }
                b = or_st.top();
                or_st.pop();
                a = or_st.top();
                or_st.pop();
                c = a - b;
                or_st.push(c);
                break;
            case '*':
                // 检测后缀表达式的合法性:操作数是否足够
                if (or_st.size() < 2)
                {
                    leg = false;
                    ill_id = 1003;
                    return -10000000000000.0;
                }
                b = or_st.top();
                or_st.pop();
                a = or_st.top();
                or_st.pop();
                c = a * b;
                or_st.push(c);
                break;
            case '/':
                // 检测后缀表达式的合法性:操作数是否足够
                if (or_st.size() < 2)
                {
                    leg = false;
                    ill_id = 1004;
                    return -10000000000000.0;
                }
                b = or_st.top();
                or_st.pop();
                a = or_st.top();
                or_st.pop();
                c = a / b;
                or_st.push(c);
                break;
            default:
                break;
            }
        }
    }
    if (or_st.size() == 1)
    {
        leg = true;
        return or_st.top();
    }
    else // 检测后缀表达式的合法性:操作数是否有多余
    {
        leg = false;
        ill_id = 2001;
        return -10000000000000.0;
    }
}

void init_src_des(vector<string>& src, vector<string>& des)
{
    src.push_back("+");
    src.push_back("-");
    src.push_back("*");
    src.push_back("/");
    src.push_back("(");
    src.push_back(")");
    
    des.push_back(" + ");
    des.push_back(" - ");
    des.push_back(" * ");
    des.push_back(" / ");
    des.push_back(" ( ");
    des.push_back(" ) ");
}

// 将中缀表达式转换后缀表达式和计算后缀表达式封装合并
double cal_inf(const vector<string>& inf, map<string, int>& ops, bool& leg_trans, bool& leg_cal, int& ill_id)
{
    leg_trans = true;
    ill_id = 0;
    vector<string> postf;
    in2post(inf, postf, ops, leg_trans, ill_id);
    if (leg_trans)
    {
        show(postf);
    }
    else
    {
        cout << "Trans illegal: " << ill_id << '!' << endl << endl;
        return -10000000000000.0;
    }
    
    leg_cal = true;
    ill_id = 0;
    double ret = cal_post(postf, ops, leg_cal, ill_id);
    if (leg_cal)
    {
        return ret;
    }
    else
    {
        cout << "Cal illegal: " << ill_id << '!' << endl << endl;
        return -10000000000000.0;
    }
}

int main()
{
    
    map<string, int> ops;
    init_op(ops);
    vector<string> inf;
    
    vector<string> src, des;
    init_src_des(src, des);
    
    while (1)
    {
        get_infix(inf, src, des);
        
        bool leg_trans = true;
        bool leg_cal   = true;
        int  ill_id    = 0;
        double ret = cal_inf(inf, ops, leg_trans, leg_cal, ill_id);
        if (leg_trans && leg_cal)
        {
            cout << ret << endl << endl;
        }
    }

    /*
    map<string, int> ops;
    init_op(ops);
    vector<string> inf, postf;
    
    vector<string> src, des;
    init_src_des(src, des);


    while (1)
    {
        get_infix(inf, src, des);
        // show(inf);
        
        bool leg_trans = true;
        int  ill_id_trans = 0;
        
        in2post(inf, postf, ops, leg_trans, ill_id_trans);
        if (leg_trans)
        {
            show(postf);
        }
        else
        {
            cout << "Trans illegal: " << ill_id_trans << '!' << endl << endl;
            continue;
        }
        
        bool leg_cal = true;
        int  ill_id_cal = 0;
        
        double ret = cal_post(postf, ops, leg_cal, ill_id_cal);
        if (leg_cal)
        {
            cout << ret << endl << endl;
        }
        else
        {
            cout << "Cal illegal: " << ill_id_cal << '!' << endl << endl;
        }
    }
    */
    
    system("PAUSE");
    return 0;
}

         讨论

         两个阶段检测的区别在于:第1阶段主要涉及操作数、操作符自身的检测;第2阶段检测的是操作数与操作符之间的关系是否合法,主要是数量上的。

         从某种意义上讲,第1阶段主要是涉及词法方面的检测,第2阶段主要是涉及语法方面的检测。

原文地址:https://www.cnblogs.com/unixfy/p/3234058.html