计算包含函数的算术表达式
算术表达式可以使用中缀表达式和后缀表达式(逆波兰表达式)计算,以下分别实现这两个方法,以下实现方法可以在算术表达式中包含部分函数,如三角函数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)——枚举符合要求的算术表达式(DFS、回溯法)
作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 题目描述: 约定按照自然优先级,并且不使用括号,在数字'0'~'9'之间加入加号'+'或乘号'* ...
- MySQL之算术表达式、聚合函数及GROUP BY 与 HANVING 等函数的应用
一.MySQL的算术表达式 算术表达式就是加减乘除的运算过程,主要是对一条数据中出现的数字进行统计和运算. 首先,有一张数据表,如下: mysql> select * from test_sco ...
- java算术表达式_一文了解如何用 Java 进行算术表达式计算
(给ImportNew加星标,提高Java技能) 编译:ImportNew/唐尤华 如何用Java计算"5+3"."10-40"."10*3" ...
- 实现一个简单的计算器,输入一个包含圆括号、加减乘除、求余等符号组成的算术表达式字符串,输出该算术表达式的值
#include <stdio.h> #include <string.h> #include <stdlib.h>int Preemption(char a, c ...
- 计算机的算数运算符号位参与计算吗,C语言算术运算符和算术表达式
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 C语言中运算符和表达式数量之多,在高级语言中是少见的.正是丰富的运算符和表达式使C语言功能十分完善.这也是C语言的主要特点之一. C语言的运算符不仅具有不 ...
- html算术计算,js实现计算器 提供算术表达式求值
//html 计算器 @import "calculator.css"; //CSS /* CSS Document */ body { /* padding-right:40 ...
- 1.3编程基础之算术表达式与顺序执行 02 计算(a+b)*c的值
http://noi.openjudge.cn/ch0103/02/ #include <iostream> #include <bits/stdc++.h> using na ...
- 1.3编程基础之算术表达式与顺序执行 03 计算(a+b) c的值
http://noi.openjudge.cn/ch0103/01/ #include <bits/stdc++.h> using namespace std; int main() {i ...
- 1.3编程基础之算术表达式与顺序执行 05 计算分数的浮点数值
#include <iostream> using namespace std; int main() {int a,b;cin>>a>>b;printf(&quo ...
最新文章
- Curr Biol:间隔学习可巩固记忆的奥秘
- 直接访问静态图片_详解nginx和tomcat访问图片和静态页面的配置方法
- python工程师一个月多少钱-苏州工业园区学编程大概多少钱一个月
- 计算机网络英语求职简历翻译,计算机网络求职英文简历模板.doc
- 被人画是怎样一种体验?
- 常用算法 之二 牛顿迭代法求解PT100温度(高阶方程求解)
- android 按键会触发ontouch吗?_这次,我把Android事件分发机制翻了个遍
- Div前台显示自动换行和不自动换行的问题
- Leetcode 206.反转链表(双指针迭代法和递归操作)
- postgres数据库入门, python 操作postgres
- Java开源J2EE框架
- GD32F450以太网(1):ETH 外设接口简介
- MeshLab怎么换背景颜色?
- 架构必知:MySQL 如何实现 ACID ?
- DNS 智能解析功能评测之华为云篇~
- java基础面经--下
- php禁止贪婪,PHP正则表达式核心技术完全详解 第11节 贪婪匹配与禁止贪婪
- Linux修改文件句柄数及vm.max_map_count、stack size
- New Phy: 中科院城环所朱永官等综述全球变化对叶际微生物组的影响
- python代码手机壁纸_Python制作微信好友背景墙教程(附完整代码)