首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于矢量C++的智能计算器

基于矢量C++的智能计算器
EN

Code Review用户
提问于 2018-01-11 15:35:15
回答 1查看 1.4K关注 0票数 4

我试着做一个智能计算器(首先做乘法、除法和模运算),这就是我最后得到的结果。

我使用了以下逻辑:我们必须得到一个值,然后是一个符号,然后是一个值,另一个符号等等。如果我们得到了其他的东西,表达式就无效了,所以我们就退出。

所有的值和运算符都在向量中,计算如下:

0-1-2

7+4*9=

--0-1

我们扫描乘法,除法和模。在符号向量中,我们得到一个'*',其中i= 1,所以我们做values.at(i) * values.at(i + 1),我们把结果放在values.at(i)中,删除values.at(i + 1)。然后我们扫描具有相同逻辑的其他操作符,因此我们将得到一个结果。

代码语言:javascript
复制
    #include <iostream>
    #include <vector>
    #include <cmath>
    using namespace std;

    void do_mult_div_mod(vector<double> &values, vector<char> &signs)
    {
        for(int i{0};i < signs.size(); ++i)
        {
            if(signs.size() != 0 && signs.at(i) == '*')
            {
                values.at(i) = values.at(i)*values.at(i+1);
                values.erase(values.begin()+i+1);
                signs.erase(signs.begin()+i);
                --i; // removed an element, so --i, then ++i so i remains at the same value
            }

            if(signs.size() != 0 && signs.at(i) == '/')
            {
                if(values.at(i+1) == 0)
                {
                    cout << "Infinite" << '\n';
                    exit(1);
                }
                values.at(i) = values.at(i)/values.at(i+1);
                values.erase(values.begin()+i+1);
                signs.erase(signs.begin()+i);
                --i;
            }

            if(signs.size() != 0 && signs.at(i) == '%')
            {
               values.at(i) = fmod(values.at(i), values.at(i+1));
               values.erase(values.begin()+i+1);
               signs.erase(signs.begin()+i);
               --i;
            }
        }
     }

double do_add_sub(vector<double> &values, vector<char> &signs)
{
    double result{values.at(0)};
    for(int i{0}; i < signs.size(); ++i)
    {
        if(signs.at(i) == '+')
            result += values.at(i+1);
        if(signs.at(i) == '-')
            result -= values.at(i+1);
    }

    return result;
}

double do_parenthesis()
{
    // get local vectors of values and signs and make a mini-expression
    int nr;
    vector<double> values;

    char op{'0'};
    vector<char> oper;

    while(op != ')')
    {
        if( cin >> nr && cin >> op)
        {
            values.push_back(nr);
            if(op == ')')
                break;

            if(op == '=' || cin.peek() == '=') // if we gan an '=' here, the expression is invalid, as we didn't get an ')' yet
            {
                cout << "Invalid Expression" << '\n';
                exit(2);
            }

            oper.push_back(op);

            char buff;
            if(cin.peek() == '(') // recursion for multiple parenthesis
            {
                cin >> buff;
                values.push_back(do_parenthesis());
                if(cin >> op)
                {
                    if(op == ')')
                        break;
                    if(op == '=')
                    {
                        cout << "Invalid Expression" << '\n';
                        exit(3);
                    }
                    else
                        oper.push_back(op);
                }
                else
                {
                    cout << "Invalid Expression" << '\n';
                    exit(4);
                }
            }
        }
        else
        {
            cout << "Invalid Expression" << '\n';
            exit(5);
        }
    }


    do_mult_div_mod(values, oper);
    double fin{do_add_sub(values, oper)};

    return fin;
}

void get_expression(vector<double> &values, vector<char> &signs)
{
    double num;
    char cha;
    for(;;)  // loop until we get an '='
    {
        if(cin >> num && cin >> cha) // check if the expression we get is valid
        {
            values.push_back(num);
            if(cha == '=')  //stop
                break;
            signs.push_back(cha);

            if(cin.peek() == '(')  // check if the next token is a parenthesis
            {
                char buffer;
                cin >> buffer;
                values.push_back(do_parenthesis()); // if yes, return the result from calculating the parenthesis
                if(cin >> cha) // after a parenthesis, we must get a sign, otherwise we throw an error
                {
                    if(cha == '=')
                        break;
                    else
                        signs.push_back(cha);
                }
                else
                {
                     cout << "Invalid Expression" << '\n';
                     exit(6);
                }
            }

        }
        else
        {
            cout << "Invalid Expression" << '\n';
            exit(7);
        }
    }
}

int main() {

    vector<double> values;
    vector<char> signs;

    get_expression(values,signs);
    do_mult_div_mod(values, signs);

    double result{do_add_sub(values, signs)};

    cout << result;

    return 0;
}

我犯了什么错误?我如何改进这段代码?

EN

回答 1

Code Review用户

回答已采纳

发布于 2018-01-12 05:18:38

总的来说,这是相当直截了当和容易阅读。我真的很喜欢把用户的表达式从它自己的函数中分离出来,而不是解析它。这是把事情分开的好办法。以下是一些额外的想法。

避免using namespace std

一般来说,您应该使用避免写作using namespace std。在需要它的类型前面编写std::仅仅是几个额外的字符。

命名

您的变量命名可能需要一些工作。在do_mult_div_mod()中,您已经将参数命名为函数valuessignsvalues是个弱名字。我通常会让它在这种情况下,因为它是一个纯粹的数学函数和论点不对应于一个现实世界的对象或概念。然而,signs是错误的。您不是传递符号(这将是正的或负的),而是传递您想要对值执行的操作。因此,我建议将它们重新命名为operands,而不是valuesoperators,而不是signs

同样,在do_parenthesis()中有一个名为nr的变量。我想这应该是个数字吧?如果是这样的话,就叫它number。那么你就有了opoper。他们是操作员和操作数吗?不,原来他们都是运算符,但你不能有两个同名变量。因此,我将所有变量重命名如下:

代码语言:javascript
复制
int nextOperand;
vector<double> operands;
char nextOperator{'0'};
vector<char> operators;

在我看来,这会使一切变得更清楚。

还有numchaget_expression()。基本上,您只是重复类型名称作为变量名,这是没有帮助的。名字应该告诉你变量代表什么。同样,它们似乎代表运算符和操作数。

处理错误

很高兴看到,您至少已经考虑过错误处理,而不是假设所有的输入都是完全有效的,而且不会出错!然而,你的处理有点极端。与其随意退出应用程序(在实际的生产代码中可能永远不允许这样做),不如将一个错误返回给调用方或throw一个异常(当然还有catch并在其他地方处理它)。它看起来是这样的:

代码语言:javascript
复制
typedef enum CalcErr {
    CE_NoError = 0,
    CE_DivideByZero = 1,
    CE_MissingParens = 2,
    // ... etc.
} CalcErr;

CalcErr do_mult_div_mod(vector<double> &values, vector<char> &signs)
{
    CalcErr result = CE_NoError;

    for (int i{0}; i < signs.size() && result == CE_NoError; ++i)
    {
        // ...etc.
        if (signs.size() != 0 && signs.at(i) == '/')
        {
            if (values.at(i+1) == 0)
            {
                result = CE_DivideByZero;
            }
            else
            {
                //... etc.
            }
        // ... etc.
    }

    return result;
}

如果这样做,也有助于将计算逻辑从结果显示中分离出来。这通常被称为“将业务逻辑与表示逻辑分离”。例如,它允许您在GUI应用程序中重用计算代码。命令行应用程序只需打印错误消息,但GUI应用程序可以使用相同的代码,然后显示错误警报。

Bugs

我在打字的时候确实发现了一些bug。例如,我尝试了这个完全有效的表达式:

(8 + (1 * (3 + (7 * 4) - 1) + 1))=

它应该评估为39 (如果我做了正确的数学)。但相反,它会生成一个无效的表达式错误。

线端部

您不需要将\n打印为字符。您可以简单地将它包含在字符串中,如下所示:

代码语言:javascript
复制
std::cout << "Infinite\n";
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/184859

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档