C/C++计算器(利用栈表达式求值,支持函数运算)

表达式求值是程序设计语言编译中的一个基本问题。它的实现就是对“栈”的典型应用。

其实现思想和数据结构书上基本一致,不同的增加的函数计算,并可以扩充,利用两个栈:一个操作数栈和一个运算符栈。

计算器C/C++实现代码:
(开发环境:Dev-Cpp编译器)

1.Express.h 代码:
#include <string>
#include <stack>
#include <cctype>
#include <cstdlib>
#include <cmath>
#define EXP_PTR_NUM 9  //运算符个数
#define EXP_FUN_NUM 9  //函数个数
#ifdef  EXP_DEBUG
    #include <iostream>
#endif 

/* 单词符号类型 */
typedef enum{
    TKT_NUMBER,    //数字 
    TKT_OPERATOR,  //运算符 
    TKT_FUNCTION,  //函数 

    TKT_ENDSIGN,   //结束符 
    TKT_UNKNOW     //未知符号 
}TokenType;

/* 表达式类 */
class Express
{
public:
    Express(const std::string &exp);  //初始化表达式 
    int getVal(double &res);          //求解值 

private:
    std::string exp;   //表达式 
    std::string token; //每次读取的单词 
    TokenType tkType;  //读取的单词类型 
    int pos, length;   //读取的位置和长度 

    /* 静态数据 */
    static std::string ptrList[];   //支持的运算符
    static int ptrArgCnt[];         //运算符参数个数

    static std::string funList[];   //支持的函数列表 
    static int funArgCnt[];         //运算符参数个数 
    static int preceMap[][EXP_PTR_NUM];      //运算优先级表 

    /* 调试输出 */
    void debug();

    /* 读取下一个单词 */
    void readToken();

    /* 比较两个运算符的优先 */
    int comaprePrece(const std::string &ptr1, const std::string &ptr2);

    /* 单步运算符计算 */
    double calculate(const std::string &ptr, double arg[]);

    /* 单步函数计算 */
    double callFun(const std::string &fun, double arg[]);

    /* 获取运算符序号 */
    int getPtrIndex(const std::string &ptr);

    /* 获取函数序号 */
    int getFunIndex(const std::string &fun);

    /* 从操作数栈获取n个参数 */
    bool getArg(std::stack<double> &opnd, double arg[], int n);
};

/* 初始化静态数据 */
std::string Express::ptrList[] = {"f(", ",", "+", "-", "*", "/", "(", ")", "#"};
int Express::ptrArgCnt[] = {0, 1, 2, 2, 2, 2, 0, 0, 0};

std::string Express::funList[] = {
    "sin(", "cos(", "tan(",
    "pow(", "pow2(", "pow3(",
    "max(", "min(", "sqrt(",
};
int Express::funArgCnt[] = {
    1, 1, 1,
    2, 1, 1,
    2, 2, 1,
};

int Express::preceMap[][EXP_PTR_NUM] = {
  //{f(, , +, -, *, /, (, ), #}
    {-1,-1,-1,-1,-1,-1,-1, 1, 1},  //f(
    { 1, 1, 1, 1, 1, 1, 1, 1, 1},  //,
    {-1, 1, 1, 1,-1,-1,-1, 1, 1},  //+
    {-1, 1, 1, 1,-1,-1,-1, 1, 1},  //-
    {-1, 1, 1, 1, 1, 1,-1, 1, 1},  //*
    {-1, 1, 1, 1, 1, 1,-1, 1, 1},  ///
    {-1, 1,-1,-1,-1,-1,-1, 0, 1},  //(
    { 1, 1, 1, 1, 1, 1, 1, 1, 1},  //)
    {-1, 1,-1,-1,-1,-1,-1,-1, 0},  //#
};

/******************************************************************************/

Express::Express(const std::string &exp)
{
    this->exp = exp + "#";
    this->token = "";
    this->pos = 0;
    this->length = 1 + exp.length(); 
}

int Express::getPtrIndex(const std::string &ptr)
{
    for (int i = 0; i < EXP_PTR_NUM; ++ i) {
        if (ptrList[i] == ptr) {
            return i;
        }
    }
    return -1;
}

int Express::getFunIndex(const std::string &fun)
{
    for (int i = 0; i < EXP_FUN_NUM; ++ i) {
        if (funList[i] == fun) {
            return i;
        }
    }
    return -1;
}

int Express::comaprePrece(const std::string &ptr1, const std::string &ptr2)
{
    int m = getPtrIndex(ptr1), n = getPtrIndex(ptr2);
    if (m == -1) {
        m = 0;
    }
    if (n == -1) {
        n = 0;
    }
    return preceMap[m][n];
}

double Express::calculate(const std::string &ptr, double arg[])
{
    switch (getPtrIndex(ptr)) {
        case 1:
            return arg[0];

        case 2:
            return arg[0] + arg[1];

        case 3:
            return arg[0] - arg[1];

        case 4:
            return arg[0] * arg[1];

        case 5:
            return arg[0] / arg[1];
    }
    return 0;
}

double Express::callFun(const std::string &fun, double arg[])
{
    switch(getFunIndex(fun)) {
        case 0:
            return sin(arg[0]);

        case 1:
            return cos(arg[0]);

        case 2:
            return tan(arg[0]);

        case 3:
            return pow(arg[0], arg[1]);

        case 4:
            return arg[0] * arg[0];

        case 5:
            return arg[0] * arg[0] * arg[0];

        case 6:
            return arg[0] > arg[1] ? arg[0] : arg[1];

        case 7:
            return arg[0] < arg[1] ? arg[0] : arg[1];

        case 8:
            return sqrt(arg[0]);
    }
    return 0;
}

void Express::readToken()
{
    while (pos < length && exp[pos] == ' ') {  //去掉前空格 
        ++ pos;
    }
    if (pos >= length) {
        tkType = TKT_ENDSIGN;
        return ;
    }
    int pos_t = pos;
    char ch = exp[pos_t ++];
    char ch_n = pos_t < length ? exp[pos_t] : 0;

    if (isdigit(ch) || (ch == '-' && isdigit(ch_n) && tkType != TKT_NUMBER)) {  //判断数字 
        if (ch == '-') {
            ++ pos_t;
        }
        while (pos_t < length && isdigit(ch = exp[pos_t])) {
            ++ pos_t;
        }
        if (ch == '.') {
            ++ pos_t;
            while (pos_t < length && isdigit(exp[pos_t])) {
                ++ pos_t;
            }
        }
        tkType = TKT_NUMBER;
    } else if (-1 != getPtrIndex(std::string(1, ch))) {  //判断运算符 
        tkType = TKT_OPERATOR;
    } else if (isalpha(ch)) {  //判断函数 
        while (pos_t < length && (isalnum(ch) || ch == '_')) {
            ch = exp[++ pos_t];
        }
        if (ch == '(') {
            ++ pos_t;
            if (-1 != getFunIndex(exp.substr(pos, pos_t - pos))) {
                tkType = TKT_FUNCTION;
            } else {
                tkType = TKT_UNKNOW;
            }
        } else {
            tkType = TKT_UNKNOW;
        }
    } else {
        tkType = TKT_UNKNOW;
    }
    token = exp.substr(pos, pos_t - pos);
    pos = pos_t;
}

bool Express::getArg(std::stack<double> &opnd, double arg[], int n)
{
    if (opnd.size() < n) {
        return false;
    }
    for (int i = n - 1; i >= 0; -- i) {
        arg[i] = opnd.top();
        opnd.pop();
    }
    return true;
}

int Express::getVal(double &res)
{
    std::stack<std::string> optr;  //算符栈 
    std::stack<double> opnd;       //算数栈 

    optr.push("#");
    pos = 0;
    readToken();

    while(tkType != TKT_ENDSIGN || !optr.empty()) {
        /*#ifdef EXP_DEBUG
            std::cout << "TkT = " << tkType << ", ";
            std::cout << "Pos = " << pos << "/" << length << ", ";
            std::cout << "Token = '" << token << "'" << std::endl;
        #endif*/

        if(tkType == TKT_UNKNOW) {
            return -1; //未知符号 
        }

        if (tkType == TKT_NUMBER) {
            opnd.push(atof(token.c_str()));
            readToken();
        } else {
            int comRes = comaprePrece(optr.top(), token);
            /*#ifdef EXP_DEBUG
                std::cout << "compare('" << optr.top() << "', '" << token << "') = " << comRes << std::endl;    
            #endif*/
            switch(comRes) {
                case -1:
                    optr.push(token);
                    readToken();
                    break;

                case 1:
                {
                    std::string ptr = optr.top();
                    optr.pop();

                    int idx = getPtrIndex(ptr), argCnt;
                    double arg[10], res; 
                    if (-1 != idx) {
                        argCnt = ptrArgCnt[idx];
                        if (argCnt) {
                            if (!getArg(opnd, arg, argCnt)) {
                                return -2;//表达式错误 
                            }
                            res = calculate(ptr, arg);
                            opnd.push(res);
                        }
                    } else {
                        idx = getFunIndex(ptr);
                        argCnt = funArgCnt[idx];
                        if (!getArg(opnd, arg, argCnt)) {
                            return -2;//表达式错误 
                        }
                        res = callFun(ptr, arg);
                        opnd.push(res);
                        readToken();
                    }
                    #ifdef EXP_DEBUG
                        if (argCnt) {
                            std::cout << "('" << ptr << "', ";
                            for(int i = 0; i < argCnt; ++ i) {
                                std::cout << arg[i];
                                if (i + 1 < argCnt) {
                                    std::cout << ", ";
                                }
                            }
                            std::cout << ") = " << res << std::endl;
                        }
                    #endif
                    break;
                }

                case 0:
                    optr.pop();
                    readToken();
                    break;
            }
        }
    }
    res = opnd.top();
    return 0;
}
2.calculate.cpp 代码:
#include <iostream>
#include <cstdio>
#define EXP_DEBUG  //开启调试 
#include "Express.h"
using namespace std;

/* 输入一行字符串 */
string InputLine()
{
    string str;
    char ch;
    while ((ch = getchar()) != '\n') {
        str += ch;
    }
    return str;
}

int main()
{    
    string str;   //sqrt(7.1 + 15/3)*max(5, min(2, 3)) - 1
    str = InputLine();
    while (str.length()) {
        Express exp(str);
        double res;
        if(exp.getVal(res)) {
            cout << "Express Error!" << endl;
        } else {
            cout << str << "=" << res << endl;
        }
        cout << endl;
        str = InputLine();
    }
    return 0;
}

注:关闭调试模式(取消EXP_DEBUG宏)可以直接返回结果不输出运算过程

运行过程:
这里写图片描述

  • 13
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值