[C++] 例题 2.7.1 用栈实现简易计算器

2019-05-23  本文已影响0人  winng伍寅

前置技能

中缀:23 + (34*45) / (5+6+7)
后缀:23 34 45 * 5 6 + 7 + / +

需求描述

概要设计

书上的类设计图可以债见了。
如果有人按着后面的表达式流程图码代码,我可以保证你能码到怀疑人生。(然而前提是能码出来(?))

下面是头文件calculator.h

#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<iostream>
#include<string>

using namespace std;

class C {
public: 
    //C();    默认构造
    //~C();   默认析构
    double calculate(string InfixExp) ;               //计算函数(炒鸡安全2333
private:
    string infix_to_postfix(string InfixExp);         //中缀转后缀
    double cal_postfix(string PostfixExp);            //计算后缀
    int priority(char op);                            //返回优先级
    double stringToDouble(string num);                //字符串转浮点数
    double cal(double num1, double num2, char op);    //计算两个数
};

#endif // CALCULATOR_H

在这里我们可以清晰地看明白这个计算器类的运行方法:在我们读入了一段中缀表达式以后,直接调用calculate(string InfixExp)函数,在calculate函数内先调用infix_to_postfix(string InfixExp)函数,将中缀表达式转为后缀表达式,然后调用cal_postfix(string PostfixExp)函数,计算后缀表达式。

函数详细设计

这里直接简单摘取书上66、67页的内容对infix_to_postfix(string InfixExp)函数和cal_postfix(string PostfixExp)函数进行说明(然而书上对压栈等操作的说明写的十分笼统,且对栈的类型定义等一律没有……)

具体实现

我觉得直接copy不能提高个人coding能力,所以我觉得如果你真的想学好coding,还是应该对照我上面十分详尽的讲解看完下面的代码,然后自己写一个,或者至少尝试自己写,不会了再跟大神们讨论讨论,刷一下自己的魅力值,对吧。

下面是calculator.cpp

#include"calculator.h"
#include<iostream>
#include<string>
#include<stack>

//计算函数
double C::calculate(string InfixExp) {
    string PostfixExp = infix_to_postfix(InfixExp);     //调用中缀转后缀
    return cal_postfix(PostfixExp);                     //返回后缀计算值
}
//中缀转后缀
string C::infix_to_postfix(string InfixExp) {
    string PostfixExp = "";
    stack<char> calculate;
    for (int i = 0; i < InfixExp.length(); i++) {
        if ((InfixExp[i] >= '0' && InfixExp[i] <= '9')|| InfixExp[i] == '.') {
            PostfixExp += InfixExp[i];                  //数字直接压栈
        }
        else {
            PostfixExp += " ";                          //操作符
            if (InfixExp[i] == '(') {                   //左括号压栈
                calculate.push(InfixExp[i]);
            }
            else if (InfixExp[i] == ')') {              //右括号
                while (!calculate.empty() && calculate.top() != '(') {
                    PostfixExp += calculate.top();      //将所有操作符弹出
                    calculate.pop();
                }
                if (calculate.empty()) {
                    std::cout << "括号不匹配" << std::endl;
                    return "error";                     //确认括号匹配
                }
                calculate.pop();                        //删除栈内左括号
            }
            else if (InfixExp[i] == '+' || InfixExp[i] == '-' ||
                InfixExp[i] == '*' || InfixExp[i] == '/') {
                while (!calculate.empty() && calculate.top() != '('
                && priority(InfixExp[i]) <= priority(calculate.top())) {
                    PostfixExp += calculate.top();      //比较优先级
                    calculate.pop();
                }
                calculate.push(InfixExp[i]);
            }
            else {
                std::cout << "非法字符" << std::endl;
                return "error";
            }
        }
    }
    while (!calculate.empty()){                         //清栈
        if(calculate.top() == '('){
            std::cout << "括号不匹配" << std::endl;
            return "error";
        }
        PostfixExp += calculate.top();
        calculate.pop();
    }
    return PostfixExp;
}
//计算后缀
double C::cal_postfix(string PostfixExp) {
    if (PostfixExp == "error")
        return 0;
    double num1,num2;
    string str;
    stack<double> calculate;
    for (int i = 0; i < PostfixExp.length(); i++) {    //遇到数字,读取并入栈
        if (PostfixExp[i] == ' ') continue;            //无视空格
        else if ((PostfixExp[i] >= '0'&&PostfixExp[i] <= '9')
                ||PostfixExp[i]=='.') {
            str = "";
            for (int j = i;(PostfixExp[j] >= '0'&&PostfixExp[j] <= '9')
                            || PostfixExp[i] == '.'; i++, j++) {
                str += PostfixExp[j];
            }
            i--;
            calculate.push(stringToDouble(str));
        }
        else {                                         //遇到操作符,提取并计算
            num1 = calculate.top();
            calculate.pop();
            num2 = calculate.top();
            calculate.pop();
            calculate.push(cal(num1, num2, PostfixExp[i]));
        }
    }
    return calculate.top();
}
//计算两个数
double C::cal(double num1, double num2, char op) {
    if (op == '+') 
        return (num2 + num1);
    else if (op == '-') 
        return (num2 - num1);
    else if (op == '*') 
        return (num2 * num1);
    else if (op == '/') {
        if(num1 == 0){
            std::cout<<"除数不能为0"<<std::endl;
            return 0;
        }
        return (num2 / num1);
    }
}
//返回优先值
int C::priority(char op) {
    switch (op) {
        case '(':             //留着这群可笑的赋值,我当时大概是脑子不清醒哈哈哈
        case ')': return 1;
        case '+':
        case '-': return 2;
        case '*':
        case '/': return 3;
        default:return 0;
    }
}
//字符串转浮点数
double C::stringToDouble(string num)
{
    bool isDec = false;       //标记是否有小数
    string real = num;        //real表示num的绝对值
    char c;
    int i = 0;
    double result = 0.0, dec = 10.0;
    unsigned long size = real.size();
    while (i < size)
    {
        c = real.at(i);
        if (c == '.')
        {                     //包含小数
            isDec = true;
            i++;
            continue;
        }
        if (!isDec)
        {
            result = result * 10 + c - '0';
        }
        else
        {                     //识别小数点
            result = result + (c - '0') / dec;
            dec *= 10;
        }
        i++;
    }
    return result;
}

下面是main.cpp

#include"calculator.h"
#include<iostream>
#include<string>

int main() {
    string temp;
    cout << "请输入表达式:" << endl;
    cin >> temp;
    C cal;
    cout << "计算结果为:" << cal.calculate(temp) << endl;
    return 0;
}

封装好的类通常可以让调用很简单……但码这个代码让我明白,像我这样的凡人想封装好这么一个类还是很难的。

谨以此文祭奠我逝去的头发。

上一篇 下一篇

猜你喜欢

热点阅读