前言:
近期复习了数据结构与算法,于是刚好就写个小程序来巩固一下自己的记忆。知道大家很烦看长篇大论,因此简单的写了下。
功能描述:
- 实现基本数学运算 +、-、*、/
- 实现带括号()复杂混合运算
- 实现开方,幂次运算(不使用官方C标准库)
- 针对输入的表达式具有异常判断及提示功能
- 可实现从文本文件中读取多个运算表达式进行计算
实现效果:
因为本程序的主要目的是为了复习数据结构与算法,因此没有写的很复杂。下图为功能1、2、3、4的实现效果,成功实现。其中@代表开方运算,^代表幂方运算
提前准备一个文件,写入需要计算的表达式,如图下:
进行测试,测试结果如下:
同时将结果存于文本文件中:
实现原理:
程序主要包括计算模块、输入处理模块、显示模块、窗口句柄设置模块等。
- 输入处理模块:
为了方便后续进行计算等操作,需要对用户输入的字符串进行简单的处理,处理结果就是每个数字(不是单个数字字符,是一组数字)。比如将输入的1+4.432*3+ (3 -1)处理为1 + 4.432 * 3 + ( 3 - 1 ),这样更方便于后期根据' '字符进行分割提前数字或操作符
- 显示模块:
显示模块没有很大的难度,就是根据Windows窗口句柄来设置输出的颜色等信息,展示一个彩色界面
- 计算模块:
计算模块就是根据处理后的字符串进行对应的计算,方法有很多,比较方便的是将输入的中缀表达式转换为后缀表达式,然后进行求值处理。比较关键的就是操作符优先级的处理规则
为了方便计算,需要将中缀表达式转为后缀表达式,转换过程利用了栈
采用主要的数据结构:栈、字符串
主要代码:
/*计算表达式*/
void calculateExpression(myStrs* expression, double* resultBuf, unsigned int* errBuf)
{
for (int i = 0; i < expression->size; i++)
{
errBuf[i] = getValue(expression->data[i].str, &resultBuf[i]);
}
}
ERROR_TYPE getValue(const char* expression, double* result)
{
ERROR_TYPE error;
if ((error = checkOperator(expression)) != NONE_ERROR) {
return error;
}
myStrs infix;
myStrs posfix;
myDoubleStack doubleStack;
initmyDoubleStack(&doubleStack);
initMyStrs(&infix, DEFAULT_SIZE);
initMyStrs(&posfix, DEFAULT_SIZE);
separateString(expression, " ", &infix);
if ((error = infixToPosfix(&infix, &posfix)) != NONE_ERROR)
{
return error;
}
for (int i = 0; i < posfix.size; i++)
{
if (!isContainOperator(posfix.data[i].str))//数字直接入栈
{
double num = atof(posfix.data[i].str);
pushmyDoubleStack(&doubleStack, num);
}
else {
if (strstr(posfix.data[i].str, "@")) {//((@4 + 2))
double a = popmyDoubleStack(&doubleStack);
pushmyDoubleStack(&doubleStack, sqrt(a));
}
else {
double a = popmyDoubleStack(&doubleStack);
double b = popmyDoubleStack(&doubleStack);
if (strstr(posfix.data[i].str, "+"))
{
pushmyDoubleStack(&doubleStack, b + a);
}
else if (strstr(posfix.data[i].str, "-")) {
pushmyDoubleStack(&doubleStack, b - a);
}
else if (strstr(posfix.data[i].str, "*")) {
pushmyDoubleStack(&doubleStack, b * a);
}
else if (strstr(posfix.data[i].str, "/")) {
if (a >= -DOUBLE_ZERO_NUM && a <= DOUBLE_ZERO_NUM)
{
return ERROR_DIVISOR_ZERO;
}
pushmyDoubleStack(&doubleStack, b / a);
}
else if (strstr(posfix.data[i].str, "^")) {
pushmyDoubleStack(&doubleStack, pow(b, a));
}
}
}
}
*result = topmyDoubleStack(&doubleStack);
destoryMyStrs(&infix);
destoryMyStrs(&posfix);
return NONE_ERROR;
}
/*中缀转后缀*/
ERROR_TYPE infixToPosfix(myStrs* infix, myStrs* posfix)
{
myCharStack charStack;
initmyCharStack(&charStack);
int index = 0;
char posfixBuf[MAX_BUF_LEN] = { 0 };
for (int i = 0; i < infix->size; i++)
{
char ch = infix->data[i].str[0];
if (!isContainOperator(infix->data[i].str))//数 或 括号
{
//如果是 括号
if (isParentheses(infix->data[i].str))
{
if (ch == '(')
{
pushmyCharStack(&charStack, ch);
}
else {// ')'
while (!isEmptymyCharStack(&charStack) && topmyCharStack(&charStack) != '(')
{
char topChar = popmyCharStack(&charStack);
posfixBuf[index++] = topChar;
posfixBuf[index++] = ' ';
}
if (isEmptymyCharStack(&charStack)) return ERROR_FORMAT;
else popmyCharStack(&charStack);
}
}
else {//是数字
strcat(posfixBuf, infix->data[i].str);
index += strlen(infix->data[i].str);
posfixBuf[index++] = ' ';
}
}//操作符
else {
//栈空 或者 栈顶是左括号 或者 优先级大于栈顶优先级,则入栈
while (!isEmptymyCharStack(&charStack) && topmyCharStack(&charStack) != '(' && lessOrEqualTop(ch, topmyCharStack(&charStack)))
{
char topChar = popmyCharStack(&charStack);
posfixBuf[index++] = topChar;
posfixBuf[index++] = ' ';
}
pushmyCharStack(&charStack, ch);
}
}
while (!isEmptymyCharStack(&charStack))
{
char topChar = popmyCharStack(&charStack);
posfixBuf[index++] = topChar;
posfixBuf[index++] = ' ';
}
if (strstr(posfixBuf, "("))
{
return ERROR_FORMAT;
}
separateString(posfixBuf, " ", posfix);
return NONE_ERROR;
}
可进一步设计方向:
-
实现超大数的 +、-、*、/运算,例如:
1821293854612893461023894562349817 * 2346189460234192384617361238946 = ?
需要考虑问题: 直接采用C语言原本的基本数据类型long long int 计算,溢出怎么半?改怎样解决?
采用字符串来存储数据
-
进一步完善交互界面设计
-
加入更多运算方式
sin、cos
等 -
Qt设计界面