一个表达式计算器

最近老师要求写一个表达式计算器,讲了算法,看上去容易,但是写起来还是有很多地方要注意呀。

-类设计

-unit类--用于存储后缀表达式

unit类我自己感觉设计得不好。设计这个类的主要目的是为了往后缀表达式中存储数据。
后缀表达式中既有数,也有符号,我想不出什么方法可以把这两种数据类型放在一起,于是设计了这个畸形的类……

    class unit {    
        bool is_number;//判断该unit是否是一个数  
        double true_number=0;//存储数  
        char true_operator='$';//存储符号。  
        int priority=5;//存储符号的优先级  
        int inv=5;//存储符号的目数  
    public:  
        unit(const double &true_number_fake);//作为数的构造函数
        unit(const char &true_operator_fake);//作为符号的构造函数
        ~unit() {};
        unit& operator=(const unit &A);
        char read_operator();//读取符号
        double read_number();//读取数
        int read_priority();//读取优先级
        int read_inv();//读取目数
        bool read_is();//判断是否是一个数
        int cal_priority();//计算优先级
        int cal_inv();//计算目数
        void show();//若为一个数,输出true_number,若为一个符号,输出true_operator
    };
-caculator类--用于计算

另外设计了一个caculator类用于计算。

class caculator {
    string Expression;//中缀表达式
    queue<unit> reverse_polish_notation;//后缀表达式
    stack<unit> operators;//运算符栈
public:
    caculator() {};
    ~caculator() {};
    void input_expression();//输入中缀表达式
    double cal_reverse_polish_notation();//计算后置表达式的值
    void switch_negative_number();//预处理负数
    void switch_into_reverse_polish_notation();//将中置表达式转换为后置表达式
};

-函数设计

负数这个情况十分复杂,没有想到好的转换方法,只能要求在输入表达式的时候将负数统统用括号括起来。后来上网发现这里提供了一种好的解决方案,有时间再实现吧。
switch_negative_number这个函数用于预处理负数。作用是将负号转换为’@’以便同减号区分开来。

void caculator::switch_negative_number() {
    for (int i = 0; i < Expression.size()-1; ++i) {
        if (Expression[i] == '('&&Expression[i + 1] == '-')
            Expression[i + 1] = '@';
    }
}

计算过程大概是先预处理负号,再将中缀表达式转换为后缀表达式,最终计算后缀表达式输出结果。
写转换函数费了好大的功夫,转换的方法也是上网查的→_→。转换方法大概是这样的:
一. 从表达式头开始扫描
二. 如果遇到数字,加入后缀表达式中
三. 如果遇到符号
(1). 若栈空,直接入栈
(2). 若栈非空
a. 若为 ‘(‘,入栈。
b. 若为 ‘)’,则依次把栈中的的运算符加入后缀表达式中,直到出现’(‘,从栈中删除’(‘。
c. 若为 除括号外的其他运算符, 当其优先级高于除’(‘以外的栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,直到一个比它优先级低的或者遇到了一个左括号为止。

这个地方表达式用string存储,运算符栈用stack存储,而转换好的后缀表达式用queue存储。
其实一开始我都用了stack类,但是在写计算后缀表达式函数的时候发现要从“左”往“右”计算,也就是说要先处理先压栈的元素,但是stack是先进后出,不可以这样处理。所以这里后缀表达式用queue类存储。
这里需要注意的是数的处理。数在string里面是以“数字”的形式存在的,我们要把一个个数字组合在一起变成一个数。这里加入了小数和负数开关,为了更好地计算数。
要注意的是,数不是一计算完就压栈,而是在遇到了符号之后再压栈,所以表达式一定要加等号,要不然有些时候最后一个数不能压栈。
我还设计了一个检测数是否改变的开关。比如说(3+4)*5这个式子。如果没有这个开关,在检测到左括号后程序就会将0(压栈的数默认为0)压栈。所以一定要设计一个开关用来检测数有没有变过,以免将多余的数压栈。
以下是转换函数:

void caculator::switch_into_reverse_polish_notation() {
    bool is_decimal = false;//小数开关
    bool is_negative = false;//负数开关
    int power = -1;//计算小数用
    double temp_number = 0;
    bool change_temp_number = false;//检测数字是否改变
    for (int x = 0; x < Expression.size(); ++x) {
        if (48 <= Expression[x] && Expression[x] <= 57) {                                             //处理数字
            if (is_decimal) {
                temp_number = temp_number + pow(10, power)*(Expression[x] - 48);
                change_temp_number = true;
            }
            else if (!is_decimal) {
                temp_number = temp_number * 10 + Expression[x] - 48;
                change_temp_number = true;
            }
        }
        else if (Expression[x] == 46)                                                                 //处理小数点
            is_decimal = true;
        else if (Expression[x] == '@')
            is_negative = true;
        else {                                                                                        //处理运算符
            if (change_temp_number) {//如果temp_number有所改变的话,就入栈。针对有时运算符在首位的情况。
                if (is_negative)
                    temp_number = 0 - temp_number;
                unit push_number(temp_number);
                reverse_polish_notation.push(push_number);
                power = -1;
                temp_number = 0;
                is_decimal = false;
                change_temp_number = false;
                is_negative = false;
            }
            if (Expression[x] == 'c' || Expression[x] == 's') {//如果该运算符为三角函数
                unit push_operator(Expression[x]);
                operators.push(push_operator);
                x += 2;
                continue;
            }
            if (Expression[x] == '(') {//若为'(',入栈
                unit push_operator(Expression[x]);
                operators.push(push_operator);
            }
            else if (Expression[x] == ')') {//若为')'
                while (operators.top().read_operator() != '(') {//直到出现'('
                    reverse_polish_notation.push(operators.top());//依次把栈中的运算符加入后缀表达式中
                    operators.pop();
                }
                    operators.pop();//从栈中删除'('
            }
            else if (Expression[x] == '=')
                break;
            else {//若为除括号以外的其他运算符
                unit temp_op(Expression[x]);
                if (operators.empty())//当栈空的时候,入栈
                    operators.push(temp_op);
                else {//当栈不为空的时候
                    if (temp_op.read_priority() > operators.top().read_priority())//当其优先级高于除'('以外的运算符时
                        operators.push(temp_op);//直接入栈
                    else if (temp_op.read_priority() <= operators.top().read_priority()) {//否则
                        bool is_temp_op_pushed = false;
                        while (!operators.empty()) {//从栈顶开始
                            if (!(temp_op.read_priority() > operators.top().read_priority() || operators.top().read_operator() == '(')) {//直到一个比它优先级低的或者遇到了一个左括号为止
                                reverse_polish_notation.push(operators.top());//依次弹出比当前处理的运算符优先级高和优先级相等的运算符
                                operators.pop();
                            }
                            else {
                                operators.push(temp_op);
                                is_temp_op_pushed = true;
                                break;
                            }
                        }
                        if (operators.empty() && !is_temp_op_pushed)
                            operators.push(temp_op);
                    }
                }
            }
        }
    }
    while (!operators.empty()) {
        reverse_polish_notation.push(operators.top());
        operators.pop();
    }
}

以下是计算函数:

double caculator::cal_reverse_polish_notation() {
    stack<unit> result;
    for (int i = reverse_polish_notation.size(); i > 0; --i) {
        if (reverse_polish_notation.front().read_is()) {
            result.push(reverse_polish_notation.front());
            reverse_polish_notation.pop();
        }
        else {
            if (reverse_polish_notation.front().read_inv() == 2) {
                double a = result.top().read_number();
                result.pop();
                double b = result.top().read_number();
                result.pop();
                double c;
                switch (reverse_polish_notation.front().read_operator()) {
                case'+':c = a + b; break;
                case'-':c = b - a; break;
                case'*':c = a*b; break;
                case'/':c = b / a; break;
                }
                unit temp(c);
                result.push(c);
                reverse_polish_notation.pop();
            }
            else if (reverse_polish_notation.front().read_inv() == 1) {
                double a = result.top().read_number();
                result.pop();
                double c;
                switch (reverse_polish_notation.front().read_operator()) {
                case'c':c = cos(a); break;
                case's':c = sin(a); break;
                }
                unit temp(c);
                result.push(c);
                reverse_polish_notation.pop();
            }
        }
    }
    return result.top().read_number();
}

要运行的时候依次执行预处理函数、转换函数、计算函数就可以啦。

int main() {
    while (1) {
        caculator joy;
        cout << "请输入表达式,负数请用括号括起来,表达式必须带=" << endl;
        joy.input_expression();
        joy.switch_negative_number();
        joy.switch_into_reverse_polish_notation();
        double bb = joy.cal_reverse_polish_notation();
        cout << endl << bb << endl;
        system("pause");
        system("cls");
    }
        return 0;
}

这是我第一次用markdown写文章,还有很多地方不熟练,希望以后可以有所长进吧:)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值