zoukankan      html  css  js  c++  java
  • 代数表达式的四则运算

    代数表达式的四则运算

             前面我们讨论了四则运算的相关情形,对于给定的中缀表达式,我们对其进行词法分析、中缀转后缀、后缀计算等过程,最终得到中缀表达式的值。

             在我们输入的中缀表达式中,我们对于每个操作数都是已知的值,不存在未知量。

             而在本文中,我们是重点讨论有关中缀表达式中存在未知量的情形,我们将含有未知量的表达式称作为代数表达式。

             比如给定一代数表达式:

    a+b-c/d*xyz

             该表达式中存在5个未知量:a、b、c、d以及xyz。我们的处理过程还是按照以前处理四则运算的情形那样,首先对输入的代数表达式进行词法分析,得到想要的操作符、已知量、未知量等。然后将中缀表达式转换为后缀表达式,最后在进行后缀表达式计算的时候,我们需要知道未知量实际的值是多少,这就需要我们对未知量指定特定的数值,最终得到代数表达式的结果。

             从上面可以看出,我们的计算过程还是分为三个步骤,只是在第三个步骤计算后缀表达式的时候,我们需要对未知量指定特定的值。

             一、词法分析

             代数表达式中的token可以分为三种:

             1.操作符:+、-、*、/、(、)

             2.已知量:常数

             3.未知量:开头为字母,后面可以为字母或数字

             我们对token检测并存放到相应的中缀表达式中,这里没有必要对token设置其对应的种类码。

             二、中缀转后缀

             具体算法流程可以参照以前的,关键是要设置一个操作符栈。

             三、后缀的计算

             设置一个操作数栈,对于未知量需要指定其特定数值。

             多说无益,下面我们给出代数表达式四则运算的程序:

    // 代数表达式的四则运算
    #include <iostream>
    #include <vector>
    #include <map>
    #include <stack>
    #include <string>
    #include <cctype>
    #include <cassert>
    using namespace std;
    
    bool is_blank(char ch)
    {
        return ch == ' ' || ch == '    ' || ch == '
    ';
    }
    
    void lexer(const string& algexp, vector<string>& infix)
    {
        infix.clear();
        char ch;
        for (auto i = 0; i < algexp.size(); /*++i*/)
        {
            if (is_blank(algexp[i]))
            {
                ++i;
                continue;
            }
    
            string token;
            ch = algexp[i];
            if (isalpha(ch)) // 未知量
            {
                token += ch;
                ++i;
                if (i >= algexp.size()) // 扫描结束
                {
                    infix.push_back(token);
                    return;
                }
                ch = algexp[i];
                while (isalnum(ch))
                {
                    token += ch;
                    ++i;
                    if (i >= algexp.size()) // 扫描结束
                    {
                        infix.push_back(token);
                        return;
                    }
                    ch = algexp[i];
                }
                infix.push_back(token);
            }
            else if (isdigit(ch))
            {
                token += ch;
                ++i;
                if (i >= algexp.size()) // 扫描结束
                {
                    infix.push_back(token);
                    return;
                }
                ch = algexp[i];
                while (isdigit(ch))
                {
                    token += ch;
                    ++i;
                    if (i >= algexp.size()) // 扫描结束
                    {
                        infix.push_back(token);
                        return;
                    }
                    ch = algexp[i];
                }
                infix.push_back(token);
            }
            else
            {
                switch (ch)
                {
                case '+':
                case '-':
                case '*':
                case '/':
                case '(':
                case ')':
                    token += ch;
                    infix.push_back(token);
                    ++i;
                    break;
    
                default:
                    ++i;
                    break;
                }
            }
        }
    }
    
    bool is_operator(const string& str, const map<string, int>& optors)
    {
        auto cit = optors.find(str);
        if (cit != optors.end())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    bool is_uncertain(const string& str)
    {
        assert(!str.empty());
        if (isalpha(str[0]))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    void in_to_post(const vector<string>& infix, vector<string>& postfix, map<string, double>& unk_num, map<string, int>& optors)
    {
        postfix.clear();
        unk_num.clear();
        stack<string> op_st;
        for (auto i = 0; i < infix.size(); ++i)
        {
            if (!is_operator(infix[i], optors))
            {
                postfix.push_back(infix[i]);
                if (is_uncertain(infix[i]))
                {
                    unk_num[infix[i]] = 0.0;
                }
            }
            else
            {
                if (infix[i] == "(")
                {
                    op_st.push(infix[i]);
                }
                else if (infix[i] == ")")
                {
                    while (!op_st.empty())
                    {
                        string tmp = op_st.top();
                        if (tmp == "(")
                        {
                            op_st.pop();
                            break;
                        }
                        else
                        {
                            postfix.push_back(tmp);
                            op_st.pop();
                        }
                    }
                }
                else // 加减乘除运算符
                {
                    if (op_st.empty())
                    {
                        op_st.push(infix[i]);
                    }
                    else
                    {
                        string tmp = op_st.top();
                        if (optors[infix[i]] > optors[tmp])
                        {
                            op_st.push(infix[i]);
                        }
                        else
                        {
                            while (!op_st.empty())
                            {
                                tmp = op_st.top();
                                if (tmp == "(")
                                {
                                    break;
                                }
                                else if (optors[tmp] >= optors[infix[i]])
                                {
                                    postfix.push_back(tmp);
                                    op_st.pop();
                                }
                                else
                                {
                                    break;
                                }
                            }
                            op_st.push(infix[i]);
                        }
                    }
                }
            }
        }
        while (!op_st.empty())
        {
            postfix.push_back(op_st.top());
            op_st.pop();
        }
    }
    
    bool is_unknown(const string& str, const map<string, double>& unk_num)
    {
        auto cit = unk_num.find(str);
        if (cit != unk_num.end())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    double cal_postfix(const vector<string>& postfix, const map<string, int>& optors, map<string, double>& unk_num)
    {
        double ret = 0.0;
        stack<double> oe_st;
        for (auto i = 0; i < postfix.size(); ++i)
        {
            if (!is_operator(postfix[i], optors))
            {
                if (is_uncertain(postfix[i]))
                {
                    oe_st.push(unk_num[postfix[i]]);
                }
                else
                {
                    oe_st.push(static_cast<double>(atof(postfix[i].c_str())));
                }
            }
            else
            {
                double a(0.0), b(0.0), c(0.0);
                switch (postfix[i][0]) // 需要虚拟机技术啊
                {
                case '+':
                    b = oe_st.top();
                    oe_st.pop();
                    a = oe_st.top();
                    oe_st.pop();
                    c = a + b;
                    oe_st.push(c);
                    break;
    
                case '-':
                    b = oe_st.top();
                    oe_st.pop();
                    a = oe_st.top();
                    oe_st.pop();
                    c = a - b;
                    oe_st.push(c);
                    break;
    
                case '*':
                    b = oe_st.top();
                    oe_st.pop();
                    a = oe_st.top();
                    oe_st.pop();
                    c = a * b;
                    oe_st.push(c);
                    break;
    
                case '/':
                    b = oe_st.top();
                    oe_st.pop();
                    a = oe_st.top();
                    oe_st.pop();
                    c = a / b;
                    oe_st.push(c);
                    break;
    
                default:
                    break;
                }
            }
        }
    
        ret = oe_st.top();
        oe_st.pop();
        return ret;
    }
    
    
    void display(const vector<string>& fix)
    {
        for (auto i = 0; i != fix.size(); ++i)
        {
            cout << fix[i] << ' ';
        }
        cout << endl;
    }
    
    void init_operators(map<string, int>& optors)
    {
        optors.clear();
        optors["+"] = 100;
        optors["-"] = 100;
        optors["*"] = 200;
        optors["/"] = 200;
        optors["("] = 500;
        optors[")"] = 000;
    }
    
    void input_unknown(map<string, double>& unk_num)
    {
        cout << "Unknowns:" << endl;
        for (auto cit = unk_num.begin(); cit != unk_num.end(); ++cit)
        {
            cout << cit->first << ' ';
        }
        cout << endl;
        cout << "Input unknowns:" << endl;
        for (auto cit = unk_num.begin(); cit != unk_num.end(); ++cit)
        {
            double tmp;
            cout << cit->first << ':';
            cin >> tmp;
            cit->second = tmp;
        }
    }
    
    void display_known(const vector<string>& infix, map<string, double>& unk_num)
    {
        for (auto i = 0; i < infix.size(); ++i)
        {
            if (is_uncertain(infix[i]))
            {
                cout << unk_num[infix[i]] << ' ';
            }
            else
            {
                cout << infix[i] << ' ';
            }
        }
        cout << endl;
    }
    
    void get_infix(string& algexp)
    {
        cin.sync(); // 清空缓冲
    
        // fflush(stdin);
    
        algexp.clear();
        getline(cin, algexp);
    }
    
    int main()
    {
        map<string, int> optors;
        init_operators(optors);
    
        while (true)
        {
            string algexp;
            get_infix(algexp);
    
            vector<string> infix;
            lexer(algexp, infix);
    
            display(infix);
    
    
            vector<string> postfix;
            map<string, double> unk_num;
    
            in_to_post(infix, postfix, unk_num, optors);
    
            display(postfix);
    
            if (!unk_num.empty())
            {
                input_unknown(unk_num);
    
                display_known(infix, unk_num);
            }
    
            double ret = cal_postfix(postfix, optors, unk_num);
    
            cout << ret << endl << endl;
        }
    
        return 0;
    }

             在程序的处理过程中,我们只是默认对正确的代数表达式进行处理,针对三个阶段的异常并未作出相关的处理。

             有关异常情况的处理,我们可以用笨的方法进行硬分析,但是这种方法就是添加标识、考虑更可能多的情况。这种方法并不是最好的办法,只是说白了是苦力活。更好的方法是设计良好的数据结构,利用更有的算法,记住编译原理方面的技术进行分析。对于第一个方法我们不再做了,第二个方法有待我们进一步了解编译原理相关方面的内容。

             另外,在程序中关于根据加减乘除四种运算符的运算,我们是直接判断运算符,用的switch-case语句来进行的。更好的方法应该利用虚拟机技术,来完成运算符的运算操作。

             整体上,上面的程序就是分为三个部分:

             1.代数式的词法分析:lexer

             2.中缀转后缀:in_to_post

             3.后缀的计算:cal_post

             在中缀转后缀的过程中,我们记录了代数式中的未知量,并在转换后,对未知量进行赋值。在后缀的计算过程中,我们根据未知量复制后的值进行运算操作,最终得到代数式的运算结果。

  • 相关阅读:
    ES6小点心第二弹——底部浮现弹窗
    ES6小点心之通用弹窗
    从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!
    【quickhybrid】如何实现一个Hybrid框架
    【开源】canvas图像裁剪、压缩、旋转
    优雅的H5下拉刷新【minirefresh】
    前端筑基篇(一)->ajax跨域原理以及解决方案
    钉钉开放与商业化团队前端大量招人
    AJAX请求真的不安全么?谈谈Web安全与AJAX的关系。
    【quickhybrid】iOS端的项目实现
  • 原文地址:https://www.cnblogs.com/unixfy/p/3274053.html
Copyright © 2011-2022 走看看