四则运算
下周要仿写计算器了,不过在那之前我们要先了解以下如何用计算机来进行简单的四则运算 ;
不过个人能力有限,负号的运算没能实现,这里就讲一下我想到的 ;
首先我们用字符串来实现四则运算,我们需要先将中缀表达式转为更适合电脑理解的后缀表达式 ;
中缀表达式
中缀表达式(Infix Expression)是一种常见的数学表达式表示方法,其中运算符位于操作数的中间。中缀表达式是人们常用的数学表达方式,例如:
算术表达式:3 + 4 * 2 - 5
带括号的表达式:(3 + 4) * (2 - 5)
带有函数调用的表达式:sqrt(9) + log(10)
中缀表达式的特点是直观易懂,但在计算机处理和求值时需要进行转换。
后缀表达式
后缀表达式,也被称为逆波兰表达式(Reverse Polish Notation,RPN),是一种数学表达式的表示方法。在后缀表达式中,运算符位于操作数的后面。
后缀表达式的特点是消除了括号,并且操作符的优先级通过操作符的位置来确定。以下是一些后缀表达式的例子:
中缀表达式:3 + 4 * 2
后缀表达式:3 4 2 * +
中缀表达式:(5 + 2) * 3 - 4
后缀表达式:5 2 + 3 * 4 -
中缀表达式:sqrt(9) + log(10)
后缀表达式:9 sqrt 10 log +
在后缀表达式中,计算的顺序是从左到右,遇到操作数直接压入栈中,遇到操作符时,从栈中弹出相应数量的操作数,进行运算,并将结果压入栈中,最终栈中的唯一元素即为表达式的结果。
如何实现中缀转后缀
这里先展示一下书上的实例:
其实这里就是利用栈先进先出,后进后出的特点,来实现优先级的区分;
直接通过下面的代码解释过程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int ordersort (char a) {
if (a == '(' || a == ')') {
return 0;
} else if (a == '+' || a == '-') {
return 1;
} else if (a == '*' || a == '/') {
return 2;
}else if (a == '.') {
return 3;
} else {
return -1;
}//判断优先级
}
int main(int argc, const char * argv[]) {
char str[1000] ;
scanf("%s",str) ;
char stack01[1000] ;
int top01 = -1 ;
char stack02[1000] ;
int top02 = -1 ;
int i = 0;//初始化一个数字栈和一个符号栈
int len = (int)strlen(str) ;
// int flag01 = 1 ;
// int flag02 = 1 ;
// double e = 0 ;
while (i < len) {//遍历输入的字符串,查找数字和符号
if (str[i] >= '0' && str[i] <= '9') {
stack01[++top01] = str[i++] ;//遇到数字就将数字压入数字栈中
if (str[i] <'0' || str[i] > '9') {
stack01[++top01] = ' ' ;//在每一个数字和运算符之间添加一个空格使它们分隔开来,不然多位数运算会多个数字混在一起 ;
}
} else if (str[i] == '(') {
stack02[++top02] = str[i++] ;
} else if (str[i] == ')') {
while (stack02[top02] != '(') {
stack01[++top01] = stack02[top02--] ;
stack01[++top01] = ' ' ;//左右括号的优先级比较特熟,所以在判断符号优先级级前提前判断左右括号,左括号直接压入符号栈,右括号则对符号栈弹栈,并将弹出的符号压入数字栈,直到栈顶为左括号;
}
top02-- ;
i++ ;
} else {
if (ordersort(str[i]) > ordersort(stack02[top02]) || top02 == -1) {
stack02[++top02] = str[i++] ;//遇到四则运算符时,比较符号和栈顶的元素的优先级,优先级比栈顶高直接压入符号栈,否则直接弹出栈顶元素压入数字栈,直到栈顶元素优先级低于。。
} else {
while (ordersort(stack02[top02]) >= ordersort(str[i]) && top02 != -1) {
stack01[++top01] = stack02[top02--] ;
stack01[++top01] = ' ' ;
}
stack02[++top02] = str[i++] ;
}
}
}
while (top02 != -1) {
stack01[++top01] = stack02[top02--] ;
stack01[++top01] = ' ' ;
}//遍历完后弹出所有的符号栈元素压入数字栈
stack01[++top01] = '\0' ;
printf("%s",stack01) ;
printf("\n") ;
对小数点的处理
其实就是在优先级的基础上,把小数点理解为一种四则运算符,无论什么时候,都会优先运算小数点来构成需要的浮点数,所以小数点的优先级最高
int ordersort (char a) {
if (a == '(' || a == ')') {
return 0;
} else if (a == '+' || a == '-') {
return 1;
} else if (a == '*' || a == '/') {
return 2;
}else if (a == '.') {
return 3;
} else {
return -1;
}
}
小数点的运算
else {
double b = numstack[top03--] ;
double a = numstack[top03--] ;
int m = 0;
int n = b ;
while (n > 1) {
n /= 10 ;
m++ ;
}
double c = a + b* pow(10, -m);
numstack[++top03] = c ;
i = i+2 ;
}
后缀表达式的运算
还是展示书上的例子,书上讲的肯定比我清楚,(主要是字太多了懒得敲。。。)
这个方法解释起来就比较简单了,就是初始化一个栈,然后遍历后缀表达式,遇到数字压入栈中,遇到符号,弹出两个数字运算后压入栈中,遍历完之后输出栈中唯一的数字 ;
double numstack[1000] ;
int top03 = -1 ;//初始化一个栈来存储数字
i = 0;
double e = 0 ;
while (i < top01) {
if (stack01[i] >= '0' && stack01[i] <= '9') {
while (stack01[i] >= '0' && stack01[i] <= '9') {
e = e* 10 + stack01[i] - '0' ;
i++ ;//获取多位数
}
numstack[++top03] = e ;
e = 0 ;
i++ ;
} else if (stack01[i] == '+') {
double b = numstack[top03--] ;
double a = numstack[top03--] ;
double c = a + b ;
numstack[++top03] = c ;//运算完成后压入栈
i = i+2 ;//跳过空格
} else if (stack01[i] == '-') {
double b = numstack[top03--] ;
double a = numstack[top03--] ;
double c = a - b ;
numstack[++top03] = c ;
i = i+2 ;
} else if (stack01[i] == '*') {
double b = numstack[top03--] ;
double a = numstack[top03--] ;
double c = a * b ;
numstack[++top03] = c ;
i = i+2 ;
} else if (stack01[i] == '/') {
double b = numstack[top03--] ;
double a = numstack[top03--] ;
double c = a / b ;
numstack[++top03] = c ;
i = i+2 ;
} else {
double b = numstack[top03--] ;
double a = numstack[top03--] ;
int m = 0;
int n = b ;
while (n > 1) {
n /= 10 ;
m++ ;
}
double c = a + b* pow(10, -m);//小数点的特殊运算
numstack[++top03] = c ;
i = i+2 ;
}
}
printf("%lf ",numstack[0]) ;
一些问题
在进行字符串的输入时,不能直接通过键盘直接输入,不清楚是不是Xcode编译器的问题,如
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char a[10] ;
scanf("%s",a) ;
printf("%s",a) ;
printf("%d",(int)strlen(a)) ;
return 0;
}
我直接通过输入法输入,它读取到的是一个右箭头,即我们输入左括号后输入法会补充一个右括号,我们只要往右移动一位,但计算机读取到得是你实际输入右括号,而不是输入法补充的);
所以上面的输入我都是复制粘贴过去的 ;
然后就是负号一直没想到比较好的方法来实现 ;