大概描述
用c++语言在vc中实现部分数学计算功能。其中实现的数学计算功能包括加减乘除运算、开方计算、自然对数运算、以10为底的对数运算、幂计算、正弦余弦计算。
由用户输入要计算的表达式,然后判断表达式是否含有未知变量,若含有未知变量则调用可以处理未知量的计算函数,否则调用一般的计算函数。
把用户输入的表达式存进一个存放中缀表达式的字符数组,再定义一个存放后缀表达式的字符数组,通过调用中缀转后缀的函数,将转为后缀的字符存进后缀表达式的字符数组。
用存放后缀表达式的字符数组调用计算函数,遇到数字时,调用读取数据的函数,把数字字符转换为double型的数。最后将答案输出。
为方便,对于未知量的处理,人为地规定了未知量的顺序和名字,规定如下:未知量的定义最多有5个,其名字和顺序如下x、y、z、m、n。
同时,为方便,常见函数的名字一律采用一个字母代替,其中sin函数用s代替,cos函数用c代替,ln函数用l代替,log10函数用g代替。
实验目的
1 用c++语言在vc中实现部分数学计算功能。其中实现的数学计算功能包括加减乘除运算、开方计算、自然对数 运算、以10为底的对数运算、幂计算、正弦余弦计算。
2 通过用户输入表达式,实现字符串的处理:实现中缀表达式转化后缀表达式,实现后缀表达式的数字字符的处 理。
3 实现后缀表达式的计算。
实验设计
1、由用户输入中缀表达式,把表达式存进一个字符数组,再定义一个字符数组用于存放后缀表达式
2、写一个将中缀表达式转为后缀表达式的函数,利用栈来处理操作符和括号
3、写一个从后缀表达式获取数据的函数
4、写一个函数,判断用户输入的表达式是否含有未知量
5、写两个计算函数,一个用于计算不含未知量的表达式,一个用于计算含有未知量的表达式
6、规定未知量的名字和顺序(本程序只定义了5个未知量,其名字依次为x、y、z、m、n)
7、调用<cmath>中的常见函数;如调用其正弦余弦函数对数函数等
实验描述
- 在主函数测试计算器,提示用户相应的函数名字缩写定义,提示用户输入表达式,并且创建两个字符数组,分别用于存放中缀 表达式和后缀表达式,再用这两个数组去调用中缀转后缀的函数,继而调用后缀表达式的计算函数,具体看main()函数。
- 中缀表达式转后缀表达式函数:创建一个字符型的栈,用于存放操作符。将存放中缀表达式的字符数组作为实参来调用此函数。然后从i=0开始读取字符数组里的元素,若遇到数字或者未知量,则调用读取数据的函数;若遇到函数缩写名时,则先将后面的数存进后缀表达式的数组,再将函数缩写名存进后缀表达式的数组;若遇到‘(’,则将其进栈;若遇到‘)’,则将栈顶的操作符存进后缀表达式的字符数组,并且出栈,直到遇到‘(’,将‘(’出栈;若遇到二目操作符,则比较此操作符和栈顶操作符的优先级,若栈顶操作符的优先级高,则将栈顶操作符放进后缀表达式,并将其出栈,一直进行此操作,直到栈里的操作符优先级比此操作符的优先级低。具体看getpostfix()函数。
- 获取操作符优先级的函数:加减的优先级定义为1,乘除的优先级定义为2,幂计算的优先级定义为3。具体看getpri()函数。
- 判断字符是否是二目操作符函数,具体看is_opr()函数。
- 判断字符是否是函数缩写名,具体看is_char()函数。
- 判断字符是否是数字或者未知量函数,具体看is_num()函数。
- 从后缀表达式读取数据函数:在读取后缀表达式的字符时,遇到数字,则调用此函数。调用此函数时,除了要传后缀表达式的数组做参数外,还要传此时的i的引用来做参数。具体看read_number()函数。
- 判断表达式是否含有未知量函数,具体看is_xyzmn()函数。
- 计算不含未知量表达式的函数:创建一个double型的数组,用于存放后缀表达式里的数据和进行计算后的中间结果。遍历存放后缀表达式的字符数组,若遇到数字,则调用读取数据的函数,并且把读取到的数据放在double型的数组中;若遇到操作符或者函数缩写名,则进行相应计算。具体看getpanswer()函数。
- 计算含有未知量表达式的函数: 计算含有未知量表达式的函数实现和计算不含未知量表达式的函数实现差不多。只不过在遍历后缀表达式数组时,若遇到未知量,则将未知量的值直接存进double型数据数组,而不存未知量。具体看geteanswer()函数。
完整代码
/*************************************************************************
> File Name: mycomputer.c
> Author: surecheun
> Mail: surecheun@163.com
> Created Time: 2017年4月18日
************************************************************************/
#include<iostream>
#include<stack>
#include<string>
#include<vector>
#include <sstream>
#include<cmath>
#define max 100
using namespace std;
//对二目操作符进行优先级的量化转变
int getpri(char c) {
switch (c) {
case '+':return 1; break;
case '-':return 1; break;//‘+’和‘-’的优先级为最低级1
case '*':return 2; break;
case '/':return 2; break;//‘*’‘/’的优先级为中级2
case'^':return 3; break;//‘^’的优先级最高,为3
default:return 0; break;
}
}
//判断字符是否是二目操作符
int is_opr(char c)
{
switch (c) {
case '+':
case '-':
case '*':
case '/':
case'^':return 1; break;
default:return 0; break;
}
}
//判断字符是否是数字或者是未知量
int is_num(char c)
{
switch (c)
{
case'0':
case'1':
case'2':
case'3':
case'4':
case'5':
case'6':
case'7':
case'8':
case'9':
case'.':
case'x':
case'y':
case'z':
case'm':
case'n':
return 1; break;
default:return 0; break;
}
}
//判断字符是否代表特殊函数,为了程序简单,取特殊函数的某个字母代替该函数(此类函数均为单目运算)
int is_char(char c)
{
switch (c)
{
case's'://sin函数
case'c'://cos函数
case'q'://开方函数
case'l'://ln函数
case'g'://log10函数
return 1; break;
default:return 0; break;
}
}
//中缀表达式改写成后缀表达式
void getpostfix(char *in, char *post)
{
stack<char> st;//创建一个字符类的栈,用于存放操作符
st.push('@');//村一个@进栈底,方便于最后判断栈里的操作符是否已经全部出栈
int i = 0;
int j = 0;
while (in[i] != '#')//‘#’为字符数组的最后一个元素
{
char ch = in[i];
if (is_num(ch))//判断是否是数字后者未知量,若是则把其放进后缀字符数组
{
post[j++] = ch;
i++;
}
else if (is_char(ch))//判断字符是否是常见(单目运算)函数的缩写,若是则将函数后面的数放进后缀字符数组,再将函数名缩写放进后缀数组
{
i++;
char ch1 = in[i];
while (is_num(ch1) == 1)
{
post[j++] = ch1;
i++;
ch1 = in[i];
}//把函数后面的数输进后缀字符数组
post[j++] = ' ';//用空格分开数和函数名
post[j++] = ch; //将函数名的缩写放进后缀表达式的数组
}
else //若字符是二目操作符或者是括号,则做以下操作
{
post[j++] = ' ';//用空格隔开操作符前后的数,便于后续读取
if (ch == '(')//如果字符是‘(’,则将字符进栈
{
st.push(ch);
}
else if (ch == ')') //若字符是‘)’,则将栈中的字符输进后缀表达式的字符数组,直至遇到‘(’
{
ch = st.top();
while (ch != '(')
{
post[j++] = ch;
st.pop();
ch = st.top();
}
st.pop(); //将‘(’出栈
}
else//若字符是二目操作符‘则做以下操作
{
int thisPri = getpri(ch); //获取操作符的优先级
char prevOpt = st.top(); //获取栈顶操作符
int prevPri = getpri(prevOpt); //获取栈顶操作符的优先级
while (thisPri <= prevPri)//比较两者的优先级,若栈顶操作符的优先级高,则将栈顶操作符放进后缀表达式,并将其出栈
{
post[j++] = prevOpt;//将栈顶操作符放进后缀表达式
st.pop(); //将其出栈
prevOpt = st.top();//取栈中新栈顶的操作符
prevPri = getpri(prevOpt);//同理获取栈中新栈顶的操作符的优先级
}
st.push(ch); //把新的操作符进栈
}
i++;
}
}
char ch = st.top();
while (ch != '@')
{
post[j++] = ch;
st.pop();
ch = st.top();
}//当栈底元素不是’@‘时,将栈顶元素放进后缀表达时的字符数组
post[j] = ' ';
}
//读取数据,将后缀表达式的字符数字转变为数
double read_number(char *dest, int *i)
{
double x = 0;
int num = 0;
int j;
while (dest[*i]<'0' || dest[*i]>'9') (*i)++;
while (dest[*i] >= '0'&&dest[*i] <= '9')
{
x = x * 10 + (dest[*i] - '0');
(*i)++;
}
if (dest[*i] == '.')//遇到小数点,则计算后面的数字个数,用于判断要把数字除以多少个10
{
(*i)++;
while (dest[*i] >= '0'&&dest[*i] <= '9')
{
num++;
x = x * 10 + (dest[*i] - '0');
(*i)++;
}
}
for (j = 0; j < num; j++)
x = x / 10;
return x;
}
//计算没有未知数的表达式
double getpanswer(char *dest)
{
double x[50];//创建一个数组,用于存放数据
int len = 0;
int i = 0;
while (dest[i] != ' ')//当字符数组中的字符不是’/0‘时
{
if (dest[i] >= '0'&&dest[i] <= '9')//遇到数字时
x[len++] = read_number(dest, &i);//调用读取数据函数,并且把i的地址传过去,这样i的值将会在函数调用后,也改变,从而使i跳到下一个类型的字符
else if (is_char(dest[i]) || is_opr(dest[i]))//假设字符是二目操作符,或者是函数名缩写,则进行计算
{
switch (dest[i])
{
case '+'://加法
x[len - 2] = x[len - 2] + x[len - 1];
len--;
i++;
break;
case '-'://加法
x[len - 2] = x[len - 2] - x[len - 1];
len--;
i++;
break;
case '*'://乘法
x[len - 2] = x[len - 2] * x[len - 1];
len--;
i++;
break;
case '/'://除法
x[len - 2] = x[len - 2] / x[len - 1];
len--;
i++;
break;
case'^'://幂运算
for (double i = 1; i < x[len - 1]; i++)
{
double y = x[len - 2];
x[len - 2] = x[len - 2] * y;
}
len--;
i++;
break;
case's'://sin函数
x[len - 1] = sin(x[len - 1]);
i++;
break;
case'c'://cos函数
x[len - 1] = cos(x[len - 1]);
i++;
break;
case'q'://开方函数
x[len - 1] = sqrt(x[len - 1]);
i++;
break;
case'l'://ln函数
x[len - 1] = log(x[len - 1]);
i++;
break;
case'g'://log10函数
x[len - 1] = log10(x[len - 1]);
i++;
break;
}
}
else i++;
}
return x[len - 1];
}
//计算含有未知量的表达式
double geteanswer(char *dest, double x1, double y1, double z1, double m1, double n1)
{
double x[50];
int len = 0;
int i = 0;
while (dest[i] != ' ')
{
if (dest[i] >= '0'&&dest[i] <= '9')
x[len++] = read_number(dest, &i);
else if (dest[i] == 'x' || dest[i] == 'y' || dest[i] == 'z' || dest[i] == 'm' || dest[i] == 'n')//如果遇到未知量,则将未知量的值直接存进数据数组,而不存未知量
{
if (dest[i] == 'x')
x[len++] = x1;
else if (dest[i] == 'y')
x[len++] = y1;
else if (dest[i] == 'z')
x[len++] = z1;
else if (dest[i] == 'm')
x[len++] = m1;
else
x[len++] = n1;
i++;
}
else if (is_char(dest[i]) || is_opr(dest[i]))//遇到操作符或者函数名字缩写,则同不含有未知量的计算一样
{
switch (dest[i])
{
case '+':
x[len - 2] = x[len - 2] + x[len - 1];
len--;
i++;
break;
case '-':
x[len - 2] = x[len - 2] - x[len - 1];
len--;
i++;
break;
case '*':
x[len - 2] = x[len - 2] * x[len - 1];
len--;
i++;
break;
case '/':
x[len - 2] = x[len - 2] / x[len - 1];
len--;
i++;
break;
case'^':
for (double i = 1; i < x[len - 1]; i++)
{
double y = x[len - 2];
x[len - 2] = x[len - 2] * y;
}
len--;
i++;
break;
case's':
x[len - 1] = sin(x[len - 1]);
i++;
break;
case'c':
x[len - 1] = cos(x[len - 1]);
i++;
break;
case'q':
x[len - 1] = sqrt(x[len - 1]);
i++;
break;
case'l':
x[len - 1] = log(x[len - 1]);
i++;
break;
case'g':
x[len - 1] = log10(x[len - 1]);
i++;
break;
}
}
else i++;
}
return x[len - 1];
}
int is_xyzmn(char *d, int j)//判断表达式是否含有未知量
{
int p;
for (int o = 0; o < j; o++)
{
if (d[o] == 'x')
{
p = 1;
break;
}
else
p = 0;
}
return p;
}
int main() {
cout << "sin请用s代替;cos请用c代替;sqrt请用q代替;ln用l代替;log10用g代替!" << endl;
cout << "如果表达式含有未知数,请按下面的顺序定义变量名字:x,y,z,m,n.(最多只允许5个变量)" << endl;
char infix[max];//创建一个字符数组,用于存放中缀表达式
char postfix[max];//创建一个字符数组,用于存放后缀表达式
int i = 0;
char ch;
while ((ch = getchar()) != '
')
{
infix[i++] = ch;//把输入的字符放进中缀表达式的数组
}
infix[i++] = '#';//放进一个标志字符,便于判断中缀表达式是否已查完
infix[i] = ' ';
getpostfix(infix, postfix);//调用中缀转后缀函数
cout << "输出后缀表达式:" << postfix << endl;
double a;//用于存放答案
if (is_xyzmn(infix, i))//判断表达式是否含有未知量,若含有,则输入未知量的取值,并且调用含有未知量表达式的计算函数
{
cout << "请按顺序输入未知数的取值,若未知数不够5个,请用0补齐:";
double a1;
double a2;
double a3;
double a4;
double a5;
cin >> a1;
cin >> a2;
cin >> a3;
cin >> a4;
cin >> a5;
a = geteanswer(postfix, a1, a2, a3, a4, a5);//调用含有为质量的计算函数
}
if (!is_xyzmn(infix, i))//若表达式没有含未知量,则调用不含未知量的计算函数
a = getpanswer(postfix);//调用不含为质量的计算函数
cout << a << endl;//输出答案
getchar();
return 0;
}
欢迎指教!