C++接尾辞式を用いた数学式の計算は小数、負数の計算をサポートする


接尾辞式の概要
接尾辞式は、簡単に言えば、演算子がオペランドの後ろにある式であり、接尾辞式には接尾辞式の括弧を取り除くことができるが、演算の優先度を維持することが重要な特徴であり、コンピュータが式を計算しやすい.数学では接尾辞式を使用しています(式には二重引用符は含まれません)
例えば「1+2*(-5)」というように、この式を接尾辞式に変えると「1 2-5*+」となります.
 
接尾辞式を手動で接尾辞式に変換する方法
ここでも、上の「1+2*(-5)」式を例にとると、まず演算の順序に従って式に括弧を付けて「(1+(2*(-5)」と処理し、演算の順序に従って「演算子を2つのオペランドの後ろの右括弧の後ろに置いて、対応する括弧を取り除く」ことを徐々に実行します.に表示されます.演算手順に従って、式の最下層の「(2*(-5)」を先に処理すると、処理後はこのように「2-5*」になり、このとき式全体が「 (1+2-5*)」(ここでは論理的にはこうです) (1+(2-5*)」「(2-5*)」はオペランドとして扱われていますが、この奥の括弧を付けなくてもいいです)、ルールに従って処理を続け、このような「1 2-5*+」になると、ここでは手動で接尾辞式を接尾辞式に変換します.手動変換のプロセスを理解することは、プログラムの変換ロジックを後で理解するのに役立ちます.
 
実現構想及びコード
まず、私が実装したExpParserのコードを貼り付けます.https://github.com/Melonl/ExpParser
ソースコードにはExpParserクラスを簡単にカプセル化し、使用時に直接ExpParserオブジェクトにparser()メソッドを呼び出し、処理する数学式を転送すると返されるdoubleタイプの結果が得られます.ソースコードのArrayとArrayStackは、手書きで書くための粗末な動的配列と動的配列に基づくスタックです.関連関数の実現構想と原理を説明する.
まず、いくつかの詳細な処理関数です.
bool isOp(string s)//        
bool isNumStr(string s)//       
bool checkInput(string &str)//           ,       
double s2d(string s)//string to double,          double
int getPriority(string opstr)//            ,          

コア関数について説明します.
splitExp(string s)
まず式string全体をオペレータ分割かどうかの関数splitExp()で返し,分割後の文字列配列を返し,コンテナは自己実現のArray動的配列である.
    Array splitExp(string s)
    {
        Array tmp;
        for (int i = 0; i < s.size(); i++)
        {

            string num;
            while (isdigit(s[i]) || s[i] == '.')
            {
                num += s[i];
                i++;
            }
            if (num.size())
            {
                tmp.addLast(num);
            }
            if (isOp(s[i]))
            {

                string ss;
                ss += s[i];
                tmp.addLast(ss);
            }
        }

        //    
        for (int i = 0; i < tmp.getSize(); i++)
        {
            if (i == 0 && tmp.getSize() > 1 && tmp[i] == "-" && isdigit(tmp[i + 1][0]))
            {
                tmp[i + 1].insert(0, "-");
                tmp.remove(i);
            }
            else if (i == 0 && tmp.getSize() > 1 && tmp[i] == "-" && tmp[i + 1][0] == '(')
            {
                string s = "0";
                tmp.addFirst(s);
            }
            else if (tmp[i] == "-" && i != 0 && !isdigit(tmp[i - 1].back()) && i != tmp.getSize() - 1 && isdigit(tmp[i + 1][0]))
            {
                if (isOp(tmp[i - 1]))
                {
                    tmp[i + 1].insert(0, "-");
                    tmp.remove(i);
                }
            }
        }

        return tmp;
    }

この関数の実現構想は,まず演算子と左右の括弧に基づいて文字列を分割し,演算子と文字列を分離することであるが,小数点の点は演算子とは考えられないが,負の記号はマイナス記号で処理される.次に、分割された文字列配列全体をスキャンして負の番号を処理します.論理は次のとおりです.
1.「-」が式の先頭に表示され、後ろに数字が続く場合、マイナス記号になり、後ろの数字文字列にマージされます.
2.「-」が式の先頭に表示され、後に左かっこが続く場合、マイナス記号となり、文字列配列全体の先頭に0を挿入して「0-(xxx)」の形式に処理する
3.「-」が左かっこの後ろに表示されている場合は、マイナス記号となり、後ろの数値文字列にマージされます.
きちんとした数学式を入力しているので、この論理にはあまり問題はないと思います.
 
toSuffixExp(Array &expstr)
この関数は分割された文字列配列を接尾辞式に変換し、文字列配列を返します.
接尾辞のコードの書き方については、このブログを参照してください.https://www.cnblogs.com/whlook/p/7143327.html
このブログはとても详しく说明されているので、自分ではこれほどはっきり言えないと思いますので、皆さんにこのブログを见てもらいましょう.私のコードもこのブログの説明を参考にして実現しました.私の実現コードを貼ってください.
    Array toSuffixExp(Array &expstr)
    {
        ArrayStack opstk;
        ArrayStack outstk;

        for (int i = 0; i < expstr.getSize(); i++)
        {
            if (isNumStr(expstr[i]))
            {
                outstk.push(expstr[i]);
                continue;
            }
            else
            {
                if (expstr[i][0] == '(')
                {
                    opstk.push(expstr[i]);
                    continue;
                }
                else if (expstr[i][0] == ')')
                {
                    while (1)
                    {
                        if (opstk.top()[0] == '(')
                        {
                            opstk.pop();
                            break;
                        }
                        else
                        {
                            outstk.push(opstk.pop());
                        }
                    }
                    continue;
                }
                else if (opstk.size() == 0 || getPriority(expstr[i]) < getPriority(opstk.top()) || opstk.top() == "(")
                {
                    opstk.push(expstr[i]);
                    continue;
                }
                else if (isOp(expstr[i]) && getPriority(expstr[i]) >= getPriority(opstk.top()))
                {
                    while (opstk.size() != 0 && getPriority(expstr[i]) >= getPriority(opstk.top()))
                    {
                        outstk.push(opstk.pop());
                    }
                    opstk.push(expstr[i]);
                }
            }
        }
        int tmpsz = opstk.size();
        for (int i = 0; i < tmpsz; i++)
        {
            outstk.push(opstk.pop());
        }
        ArrayStack tmpstk;
        Array res;
        int size = outstk.size();
        for (int i = 0; i < size; i++)
        {
            tmpstk.push(outstk.pop());
        }
        while (tmpstk.size() != 0)
        {
            res.addLast(tmpstk.pop());
        }

        return res;
    }

 
cal(string &num2, string &num1, string &op)
この関数は接尾辞式の計算を実現しているが,具体的な論理はまだ簡単で,パラメータのnum 2が最初のオペランド,num 2が2番目のオペランドであることに注意しなければならない.コードは次のとおりです.
    string cal(string &num2, string &num1, string &op)
    {
        //cout << "cal:" << num1 << "," << num2 << endl;
        double n1, n2, res;
        if (op.size() != 1)
        {
            cout << "cal error: " << op << endl;
            return "";
        }

        switch (op[0])
        {
        case '+':
            res = s2d(num1) + s2d(num2);
            break;

        case '-':
            res = s2d(num1) - s2d(num2);
            break;

        case '*':
            res = s2d(num1) * s2d(num2);
            break;

        case '/':
            res = s2d(num1) / s2d(num2);
            break;

        case '^':
            res = pow(s2d(num1), s2d(num2));
            break;

        default:
            cout << "cal error: not find operator for " << op[0] << endl;
            break;
        }
        return to_string(res);
    }

 
まとめ
総じて接尾辞式の計算は比較的簡単で、論理が少し煩雑な点だけで、辛抱強く手順を整理すれば実現できます.ExpParserというプロジェクトについては、後期に対数、ルート、余剰、階乗などの演算のサポートを試してみるかもしれません.役に立つと感じたらgithub starで私のプロジェクトを見て、読んでくれてありがとう.