算术表达式可以使用中缀表达式和后缀表达式(逆波兰表达式)计算,以下分别实现这两个方法,以下实现方法可以在算术表达式中包含部分函数,如三角函数sin等。

由于普通的算术操作符(如“+-*/”)只包含一个字符,而函数会包含多个字符,为了方便先将函数用一个FunctionId的枚举替代,该枚举从1开始,算术表达式一般为明文字符串,这些枚举数只要使用20以下的数字就不会发生冲突,只是打印出来会是乱码,计算不会受影响。

enum FunctionId
{None = 0,Sin = 1,Cos = 2,Tan = 3,Cot = 4,FunctionMax,
};

然后定义函数和运算符的结构FunctionInfo和OperatorInfo,以及一些全局变量和方法。


struct FunctionInfo
{FunctionId funcId;char funcStr[10];//函数名,如"sin"int bytesOfStr;//函数名字节数,如"sin"为3字节FunctionInfo(FunctionId InFuncId, const char* InFuncStr, int InBytes = 0){funcId = InFuncId;memset(funcStr, 0, 10);bytesOfStr = InBytes > 0 ? InBytes : strlen(InFuncStr);memcpy(funcStr, InFuncStr, bytesOfStr);}static FunctionInfo None;
};
FunctionInfo FunctionInfo::None = FunctionInfo(FunctionId::None, "", 0);FunctionInfo g_functionInfos[] =
{FunctionInfo(FunctionId::Sin,   "sin",   3),FunctionInfo(FunctionId::Cos,   "cos",  3),FunctionInfo(FunctionId::Tan,   "tan", 3),FunctionInfo(FunctionId::Cot,   "cot", 3),FunctionInfo::None
};
//判断是否是函数
FunctionInfo GetFunctionInfo(const char* InExp)
{for (int i = 0; i < FunctionId::FunctionMax; i++){if (g_functionInfos->funcId == FunctionId::None){return FunctionInfo::None;}if (memcmp(InExp, g_functionInfos[i].funcStr, g_functionInfos[i].bytesOfStr) == 0){return g_functionInfos[i];}}return FunctionInfo::None;
}struct OperatorInfo
{char ch;//运算符int priority;//优先级OperatorInfo(char InChar, int InPriority){ch = InChar;priority = InPriority;}
};
OperatorInfo g_operatorInfos[] =
{OperatorInfo('(',   0),OperatorInfo(')',   0),OperatorInfo('+',   1),OperatorInfo('-',   1),OperatorInfo('*',   2),OperatorInfo('/',   2),OperatorInfo('%',   2),OperatorInfo('^',   3),OperatorInfo('\0',  -1)
};
//判断是否为操作符
bool IsOperator(char c)
{for (int i = 0;; i++){if (g_operatorInfos[i].ch == c){return true;}if (g_operatorInfos[i].ch == '\0'){return false;}}return false;
}
//获取操作符优先级
int GetPriority(char c)
{//函数优先级最高if (c > FunctionId::None && c < FunctionId::FunctionMax){return 10;}for (int i = 0;; i++){if (g_operatorInfos[i].ch == c){return g_operatorInfos[i].priority;}if (g_operatorInfos[i].ch == '\0'){return -1;}}return -1;
}bool IsNumber(char c)
{return c >= '0' && c <= '9';
}
//是否为数字,包括小数点
bool IsNumberOrDot(char c)
{return IsNumber(c) || (c == '.');
}

1、中缀表达式计算

中缀表达式需要一个操作符栈和操作数栈,多个字符的函数被替换为一个字节存放到操作符栈中,判断时只需判断是否大于None且小于FunctionMax,计算值时操作数只有一个。


double CalcValueByInfixExpression(const char* expression)
{std::stack<char> operatorStack;//操作符栈,包括普通运算符和函数std::stack<double> dataStack;//操作数栈const char* iterExp = expression;if (*iterExp == '-')//判断第一个符号是否为'-',如果是就按"0-..."处理{dataStack.push(0);operatorStack.push('-');iterExp++;}while (*iterExp != '\0'){if (*iterExp == ' '){iterExp++;continue;}FunctionInfo funcInfo = GetFunctionInfo(iterExp);if (funcInfo.funcId > FunctionId::None){//函数直接入栈operatorStack.push(funcInfo.funcId);iterExp += funcInfo.bytesOfStr;}else if (IsOperator(*iterExp)){char ch = *iterExp;if (ch == ')'){//遇到右括号,则弹出之前的运算符直到遇到左括号'(',并将计算数据保存到栈顶while (!operatorStack.empty() && operatorStack.top() != '('){char topOper = operatorStack.top();if (topOper > FunctionId::None && topOper < FunctionId::FunctionMax){//函数只有一个操作数,有多个操作数的函数不考虑double topData = dataStack.top();dataStack.top() = CalcFunctionValue(topData, (FunctionId)topOper);}else{//运算符有两个操作数,其他情况不考虑double rightData = dataStack.top();dataStack.pop();double leftData = dataStack.top();dataStack.top() = CalcOperatorValue(leftData, rightData, topOper);}operatorStack.pop();}operatorStack.pop();if (!operatorStack.empty()){char topOper = operatorStack.top();//函数后跟的都是'(',所以要判断一下下一个运算符是不是函数if (topOper > FunctionId::None && topOper < FunctionId::FunctionMax){double topData = dataStack.top();dataStack.top() = CalcFunctionValue(topData, (FunctionId)topOper);operatorStack.pop();}}}else if (ch == '-' && *(iterExp - 1) == '('){//'('后也可能跟负数,也当成"0-..."处理//不考虑'-'前有空格的情况,可提前将表达式的空格去掉dataStack.push(0);operatorStack.push('-');}else if (operatorStack.empty() || ch == '(' || GetPriority(ch) > GetPriority(operatorStack.top())){//这三种情况直接将运算符入栈operatorStack.push(ch);}else{//优先级高于或等于ch优先级的出栈进行运算,最后将ch入栈while (!operatorStack.empty() && GetPriority(ch) <= GetPriority(operatorStack.top())){char topOper = operatorStack.top();if (topOper > FunctionId::None && topOper < FunctionId::FunctionMax){dataStack.top() = CalcFunctionValue(dataStack.top(), (FunctionId)topOper);}else{double rightData = dataStack.top();dataStack.pop();double leftData = dataStack.top();dataStack.top() = CalcOperatorValue(leftData, rightData, topOper);}operatorStack.pop();}operatorStack.push(ch);}iterExp++;}else if (IsNumberOrDot(*iterExp)){//操作数入栈char buf[20] = { 0 };char* iterDest = buf;while (IsNumberOrDot(*iterExp)){*iterDest++ = *iterExp++;}double opData = atof(buf);dataStack.push(opData);}else{return 0;//表达式有误}}//将操作符都出栈运算,这里不会有函数,因为函数后都跟'(',在'('出栈的时候已经计算了while (!operatorStack.empty()){double rightData = dataStack.top();dataStack.pop();double leftData = dataStack.top();dataStack.top() = CalcOperatorValue(leftData, rightData, operatorStack.top());operatorStack.pop();}return dataStack.top();
}

2、后缀表达式计算

后缀表达式分为两步,先将中缀表达式转为后缀表达式,然后计算后缀表达式,运算符的处理上与中缀表达式有很多相似之处。


//InExpression为原表达式,OutSuffixExp为后缀表达式
bool ExpressionToSuffixExp(const char* InExpression, char* OutSuffixExp)
{std::stack<char> operatorStack;//操作符栈,包括普通运算符和函数const char* iterInfix = InExpression;char* iterSuffix = OutSuffixExp;if (*iterInfix == '-')//判断第一个符号是否为'-',如果是就按"0-..."处理{*iterSuffix++ = '0';*iterSuffix++ = ' ';operatorStack.push('-');iterInfix++;}while (*iterInfix != '\0'){if (*iterInfix == ' '){iterInfix++;continue;}FunctionInfo funcInfo = GetFunctionInfo(iterInfix);if (funcInfo.funcId > FunctionId::None){operatorStack.push(funcInfo.funcId);iterInfix += funcInfo.bytesOfStr;}else if (IsOperator(*iterInfix)){char ch = *iterInfix;if (ch == ')'){//遇到右括号,则弹出之前的运算符直到遇到左括号'(',并将运算符加入到后缀表达式while (!operatorStack.empty() && operatorStack.top() != '('){*iterSuffix++ = operatorStack.top();operatorStack.pop();}if (!operatorStack.empty()){operatorStack.pop();}}else if (ch == '-' && *(iterInfix - 1) == '('){//'('后也可能跟负数,也当成"0-..."处理//不考虑'-'前有空格的情况,可提前将表达式的空格去掉*iterSuffix++ = '0';*iterSuffix++ = ' ';operatorStack.push('-');}else if (operatorStack.empty() || ch == '(' || GetPriority(ch) > GetPriority(operatorStack.top())){//这三种情况直接将运算符入栈operatorStack.push(ch);}else{//优先级高于或等于ch优先级的出栈,并加入后缀表达式,最后将ch入栈while (!operatorStack.empty() && GetPriority(ch) <= GetPriority(operatorStack.top())){*iterSuffix++ = operatorStack.top();operatorStack.pop();}operatorStack.push(ch);}iterInfix++;}else if (IsNumberOrDot(*iterInfix)){while (IsNumberOrDot(*iterInfix)){*iterSuffix++ = *iterInfix++;}*iterSuffix++ = ' ';//用空格分隔操作数}else{return false;//表达式有误}}//将剩余操作符出栈加入后缀表达式while (!operatorStack.empty()){*iterSuffix++ = operatorStack.top();operatorStack.pop();}*iterSuffix = '\0';return true;
}double CalcSuffixValue(const char* InSuffixExp)
{std::stack<double> dataStack;//操作数栈const char* iterExp = InSuffixExp;while (*iterExp != '\0'){if (*iterExp == ' '){iterExp++;continue;}//计算后缀表达式只需要区分操作数和操作符即可if (IsNumberOrDot(*iterExp)){int t = 0;char buf[20];while (IsNumberOrDot(*iterExp)){buf[t++] = *iterExp++;}buf[t] = '\0';double d = atof(buf);dataStack.push(d);}else{if (*iterExp > FunctionId::None && *iterExp < FunctionId::FunctionMax){double topData = dataStack.top();dataStack.top() = CalcFunctionValue(topData, (FunctionId)* iterExp);}else{double rightData = dataStack.top();dataStack.pop();double leftData = dataStack.top();dataStack.top() = CalcOperatorValue(leftData, rightData, *iterExp);}iterExp++;}}if (dataStack.empty()){return 0;//表达式有误才会进到这里}return dataStack.top();
}double CalcValueBySuffixExpression(const char* expression)
{//分两步,先转为后缀表达式,后计算表达式char suffixExp[100] = { 0 };ExpressionToSuffixExp(expression, suffixExp);return CalcSuffixValue(suffixExp);
}

由于算术运算符手动输入,会有各种各样的形式,甚至是输入错误的表达式,这里还有很多特殊情况没有考虑到,只处理一些比较简单的情况。另外如果要添加其他函数需在枚举(FunctionId)、全局数组g_functionInfos以及函数CalcFunctionValue中增加相关代码。

最后使用控制台程序测试。

int main()
{char expression[100];printf("请输入表达式:\n");gets_s(expression, 100);printf("表达式计算结果为:\n");printf("CalcValueByInfixExpression:%f\n", CalcValueByInfixExpression(expression));printf("CalcValueBySuffixExpression:%f\n", CalcValueBySuffixExpression(expression));system("pause");return  0;
}

输出结果:

计算包含函数的算术表达式相关推荐

  1. 精选算法题(1)——枚举符合要求的算术表达式(DFS、回溯法)

    作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 题目描述: 约定按照自然优先级,并且不使用括号,在数字'0'~'9'之间加入加号'+'或乘号'* ...

  2. MySQL之算术表达式、聚合函数及GROUP BY 与 HANVING 等函数的应用

    一.MySQL的算术表达式 算术表达式就是加减乘除的运算过程,主要是对一条数据中出现的数字进行统计和运算. 首先,有一张数据表,如下: mysql> select * from test_sco ...

  3. java算术表达式_一文了解如何用 Java 进行算术表达式计算

    (给ImportNew加星标,提高Java技能) 编译:ImportNew/唐尤华 如何用Java计算"5+3"."10-40"."10*3" ...

  4. 实现一个简单的计算器,输入一个包含圆括号、加减乘除、求余等符号组成的算术表达式字符串,输出该算术表达式的值

    #include <stdio.h> #include <string.h> #include <stdlib.h>int Preemption(char a, c ...

  5. 计算机的算数运算符号位参与计算吗,C语言算术运算符和算术表达式

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 C语言中运算符和表达式数量之多,在高级语言中是少见的.正是丰富的运算符和表达式使C语言功能十分完善.这也是C语言的主要特点之一. C语言的运算符不仅具有不 ...

  6. html算术计算,js实现计算器 提供算术表达式求值

    //html 计算器 @import "calculator.css"; //CSS /* CSS Document */ body { /*   padding-right:40 ...

  7. 1.3编程基础之算术表达式与顺序执行 02 计算(a+b)*c的值

    http://noi.openjudge.cn/ch0103/02/ #include <iostream> #include <bits/stdc++.h> using na ...

  8. 1.3编程基础之算术表达式与顺序执行 03 计算(a+b) c的值

    http://noi.openjudge.cn/ch0103/01/ #include <bits/stdc++.h> using namespace std; int main() {i ...

  9. 1.3编程基础之算术表达式与顺序执行 05 计算分数的浮点数值

    #include <iostream> using namespace std; int main() {int a,b;cin>>a>>b;printf(&quo ...

最新文章

  1. Curr Biol:间隔学习可巩固记忆的奥秘
  2. 直接访问静态图片_详解nginx和tomcat访问图片和静态页面的配置方法
  3. python工程师一个月多少钱-苏州工业园区学编程大概多少钱一个月
  4. 计算机网络英语求职简历翻译,计算机网络求职英文简历模板.doc
  5. 被人画是怎样一种体验?
  6. 常用算法 之二 牛顿迭代法求解PT100温度(高阶方程求解)
  7. android 按键会触发ontouch吗?_这次,我把Android事件分发机制翻了个遍
  8. Div前台显示自动换行和不自动换行的问题
  9. Leetcode 206.反转链表(双指针迭代法和递归操作)
  10. postgres数据库入门, python 操作postgres
  11. Java开源J2EE框架
  12. GD32F450以太网(1):ETH 外设接口简介
  13. MeshLab怎么换背景颜色?
  14. 架构必知:MySQL 如何实现 ACID ?
  15. DNS 智能解析功能评测之华为云篇~
  16. java基础面经--下
  17. php禁止贪婪,PHP正则表达式核心技术完全详解 第11节 贪婪匹配与禁止贪婪
  18. Linux修改文件句柄数及vm.max_map_count、stack size
  19. New Phy: 中科院城环所朱永官等综述全球变化对叶际微生物组的影响
  20. python代码手机壁纸_Python制作微信好友背景墙教程(附完整代码)

热门文章

  1. android手机量体温,手机体温测试仪软件
  2. kindle 6寸格式转换
  3. 亨通光电和量子计算机进展,$亨通光电(SH600487)$
  4. 复试上机指南之小技巧篇(1)
  5. Python自然语言处理第一章 - 语言处理与Python
  6. Ubuntu和Windows双系统连接罗技蓝牙键盘
  7. 大数据专家舍恩伯格演讲
  8. 八大主流云管理平台对比导购
  9. 一次给人重装 Win7 恶心之路
  10. 前端使用js实现Rsa的加密和解密