您的位置:首页 > 编程语言 > C语言/C++

利用栈Stack实现计算器实验设计(C++)

2017-02-25 19:02 1726 查看

一、实验思路

1.输入中缀式。

输入格式:string,包括数字、运算符号(包括括号和英文运算函数名如sin等)。

2.将中缀式转换为后缀式。

输入:中缀式 格式:string

方法:栈后进先出原理

栈名臣:符号栈

输出:后缀式 格式:string,其中数字和运算符号中间有空格,无括号。

3.将后缀式字符串分成子字符串逐个识别并运算。

输入:后缀式 格式:string

方法:stringstream、栈后进先出原理

栈名称:数据栈

输出:数字 格式:double

解释:每个子字符串只可能是数字或者为运算符,若为数据则压入数据栈,若为运算符,则对栈顶元素运算并且将运算结果存入栈中。

二、 实验步骤及代码实现

分步解决以下问题:

1.运算符的优先级问题

运算符优先级函数及实现代码

int priority( char ch )
{
int p = -3;//初始化
if( int( ch ) >= 48 && int( ch ) <= 57 || ch == 'e' || ch == 'p' ) p = -1;
//令数字和代表自然数e和pi的字母优先级为-1
else if( ch == '+' || ch == '-' ) p = 1;
//令加减符号优先级为1
else if( ch == '*' || ch == '/' ) p = 2;
//令乘除符号优先级为2
else if(  ch == '^' ) p = 3;
//令^符号优先级为3
else if( ch == 's' || ch == 'c' || ch == 't' || ch == 'l' || ch == 'o' ) p = 5;
//令指定的几个代表sin、cos、tan、lg和log的函数字母优先级为5。
return p;
}


3.中缀式到后缀式的转换(用堆栈实现转换)

中缀式到后缀式的转换的函数string infix_to_uffix( string s )以及解决以下问题的代码实现及解释

(分各类情况,不完整代码):

1.构造栈、确定压栈的方式

2.括号形式转换

3.小数形式转换

4.负数形式转换

5.带英文字母的函数的转换

为实现更好的中缀式转换后缀式,首先先将输入的字符串经过change函数加工:

(举例:输入(sin(2)+3)*4,转换为(s2+3)*4,此时英文字母函数类同于运算符,以便更好压栈)

string change( string st0 )
{
string st;
for( int i = 0; i < st0.size(); i++ )
{
if( priority( st0[ i ]) == -1 || priority( st0[ i ]) >=1 && priority( st0[ i ]) < 5 || st0[ i ] == '(' || st0[ i ] == ')' || st0[ i ] == '.' )
{
st.push_back( st0[ i ] );
}
else if( st0[ i ] == 's' && st0[ i + 1 ] == 'i' )
{
st.push_back( 's' );
}
else if( st0[ i ] == 'c' )
{
st.push_back( 'c' );
}
else if( st0[ i ] == 't' )
{
st.push_back( 't' );
}
else if( st0[ i ] == 'l' && st0[ i + 1 ] == 'g' )
{
st.push_back( 'l' );
}
else if( st0[ i ] == 'l' && st0[ i + 1 ] == 'o' )
{
st.push_back( 'o' );
}
else if( st0[ i ] == 'p' && st0[ i + 1 ] == 'i' )
{
st.push_back( 'p' );
}
else if( st0[ i ] == 'e' )
{
st.push_back( 'e' );
}
else if( st0[ i ] == 'a' && st0[ i + 1 ] == 'b' )
{
st.push_back( 'a' );
}
else;
}
return st;
}


下面开始进入中缀式转后缀式的过程:

string infix_to_uffix( string s )
{
string str0;
stack< char > mystack;//建立一个符号栈
int mark = 0;//标记


如果出现输入”-2”的情况则在“中缀式字符串”前面加多一个0,变为”0-2”的情形。

if( s[ 0 ] == '-' )
{
string s0 = "0";
s = s0 + s;
}


下面对于每一输入:

如果括号中是负数(“(-2)”形式),则标记值加一。且当读到”-“时不操作。

if( s[ i ] == '(' && s[ i + 1 ] == '-' )
{
str0.push_back( ' ' );
str0.push_back( '-' );
mark ++;
}
else if( s[ i ] == '-' && s[ i - 1 ] == '(' );


当读取的字符不为右括号:

(1)当字符为左括号:

if( s[ i ] == '(' )
{
str0.push_back( ' ' );
mystack.push( '(' );
}


(2)当字符为数字和小数点时

if( priority( s[ i ] ) == -1 || s[ i ] == '.'  )
str0.push_back( s[ i ] );


(3)当字符为运算符(包括左括号)

 a.当符号栈为空,直接压栈

str0.push_back( ' ' );
if( mystack.empty() )
mystack.push( s[ i ] );


 b1.当输入的符号优先级高于“符号栈”栈顶符号优先级时直接压入“符号栈”。

if( priority( s[ i ] ) > priority( mystack.top() ) )
{
str0.push_back( ' ' );
mystack.push( s[ i ] );
}


 b2.当输入的符号优先级等于符号栈栈顶符号优先级时先放出栈顶的符号到“后缀式字符串”中并且出栈再把输入的符号压栈。

if( priority( s[ i ] ) == priority( mystack.top() ) )
{
str0.push_back( mystack.top() );
mystack.pop();
str0.push_back( ' ' );
mystack.push( s[ i ] );
}


 b3.当输入的符号优先级小于栈顶元素则一直把“符号栈”中的符号放入“后缀式字符串”并出栈直到碰见”(“。若”(“不存在则一直进行到栈为空。

if( priority( s[ i ] ) < priority( mystack.top() ) )
{
while( mystack.size() != 0 )
{
if( mystack.top() == '(' )
str0.push_back( ' ' );
str0.push_back( mystack.top() );
mystack.pop();
}
}


(4)当符号为右括号

 若括号前为负数形式(”(-2)”形式)(if)。每匹配一次括号,标记值减一,直到为零即可判断栈底无表示负数类的左括号。

 若括号中为表达式的格式(”(2+3)”形式)(else)。将所有的在栈中直到左括号为止的所有符号全部出栈并且放入后缀式字符串,并删去栈中的”(“。

if( mark != 0 )
mark--;
else
{
while( mystack.top() != '(' )
{
str0.push_back( ' ' );
str0.push_back( mystack.top() );
mystack.pop();
}
mystack.pop();
}


输入到中缀式最后时将符号栈内所有的符号逐个放入“后缀字符串”中并出栈。

while( mystack.size() != 0 )
{
str0.push_back( ' ' );
str0.push_back( mystack.top() );
mystack.pop();
}


最后返回str0即可。

中缀式转后缀式测试例子:

1.

输入中缀式: (sin(2)+3)*4+5^2

输出后缀式:2 s 3 + 4 * 5 2 ^ +

2.

输入中缀式:(cos(3))^2+(sin(3))^2

输出后缀式:3 c 2 ^ 3 s 2 ^ +

3.运算的实现

(1)定义运算函数(例如加法、sin函数)

//加法运算
void aadd()
{
ans = container.top();
container.pop();
ans += container.top();
container.pop();
container.push( ans );
}

//sin运算
void ssin()
{
ans = sin( container.top() );
container.pop();
container.push( ans );
}


(2)将字符串分割并分别识别子字符串并作出操作:

数字子字符串压入“数据栈”,识别符号子字符串则对“数据栈”中的数据进行运算操作。

string str;
while( cout<<">> " && cin >> str )
{
stringstream ss( infix_to_uffix( str ) );
string sp;
while( ss >> sp )//将后缀表达式字符串分割成子字符串并一个一个将子字符串输入
{
if( int( sp[ 0 ] ) > 47 && int( sp[ 0 ] ) < 58 )//当子字符串为数字,则把数字转为double格式压入数据栈
{
container.push( exchange( sp ) );
}
else if( sp[ 0 ] == '-' && sp.size() != 1 )//当为(-string)格式时
{
if( int( sp[ 1 ] ) > 47 && int( sp[ 1 ] ) < 58 )
{
container.push( exchange( sp ) );
}//当string都为数字,则把数字转为double格式压入数据栈
else if( sp[ 1 ] == 'p' )
{
double pi = 3.14159265358979323846264;
container.push( -pi );
}//当string为-p时将-3.14159265358979323846264压入数据栈
else if( sp[ 1 ] == 'e' )
{
container.push( -2.71828182846 );
}//当string为-e时将-2.71828182846压入数据栈
}
else if( sp[ 0 ] == 'p' )
{
double pi = 3.14159265358979323846264;
container.push( pi );
}//当子字符串为"p",则把数字"3.1415926535897932384"压入数据栈
else if( sp[ 0 ] == 'e' )
{
container.push( 2.71828182846 );
}//当子字符串为"e",则把数字"2.71828182846"压入数据栈
else if( sp[ 0 ] == '+' )
{
aadd();
}//当子字符串为"+",调用aadd函数(下面以此类推)
else if( sp[ 0 ] == '-' )
mminus();
else if( sp[ 0 ] == '*' )
mmul();
else if( sp[ 0 ] == '/' )
ddiv();
else if( sp[ 0 ] == '^' )
ppow();
else if( sp[ 0 ] == 's' )
ssin();
else if( sp[ 0 ] == 'c' )
ccos();
else if( sp[ 0 ] == 't' )
ttan();
else if( sp[ 0 ] == 'l' )
llg();
else if( sp[ 0 ] == 'o' )
llog();
else if( sp[ 0 ] == 'a' )
aabs();
else;
}


4.输出结果,并规定精度。

if( container.top() > 0 && container.top() < pow( 10.0,-8 ) || container.top() < 0 && container.top() > 0-pow( 10.0,-8 ) )
cout << "result:"  << 0 << endl;
else
printf("result:%.8g\n",container.top());
cout<<endl;


附:完整代码:

#include<iostream>
#include<string>
#include<sstream>
#include<vector>
#include<stack>
#include<cmath>
#include<algorithm>
#include<iomanip>
using namespace std;

stack< double > container;
double ans;

//规定优先级
int priority( char ch )
{
int p = -3;
if( int( ch ) >= 48 && int( ch ) <= 57 || ch == 'e' || ch == 'p' ) p = -1;
else if( ch == '+' || ch == '-' ) p = 1;
else if( ch == '*' || ch == '/' ) p = 2;
else if( ch == '^' ) p = 3;
else if( ch == 's' || ch == 'c' || ch == 't' || ch == 'l' || ch == 'o' ) p = 5;
return p;
}

string change( string st0 ) { string st; for( int i = 0; i < st0.size(); i++ ) { if( priority( st0[ i ]) == -1 || priority( st0[ i ]) >=1 && priority( st0[ i ]) < 5 || st0[ i ] == '(' || st0[ i ] == ')' || st0[ i ] == '.' ) { st.push_back( st0[ i ] ); } else if( st0[ i ] == 's' && st0[ i + 1 ] == 'i' ) { st.push_back( 's' ); } else if( st0[ i ] == 'c' ) { st.push_back( 'c' ); } else if( st0[ i ] == 't' ) { st.push_back( 't' ); } else if( st0[ i ] == 'l' && st0[ i + 1 ] == 'g' ) { st.push_back( 'l' ); } else if( st0[ i ] == 'l' && st0[ i + 1 ] == 'o' ) { st.push_back( 'o' ); } else if( st0[ i ] == 'p' && st0[ i + 1 ] == 'i' ) { st.push_back( 'p' ); } else if( st0[ i ] == 'e' ) { st.push_back( 'e' ); } else if( st0[ i ] == 'a' && st0[ i + 1 ] == 'b' ) { st.push_back( 'a' ); } else; } return st; }

//测试中缀式转化为后缀式
string infix_to_uffix( string s )
{
string str0;
stack< char > mystack;//建立一个符号栈

int mark = 0;
if( s[ 0 ] == '-' )
{
string s0 = "0";
s = s0 + s;
}
int size = s.size();
for( int i = 0; i < size; i++ )
{
if( s[ i ] == '(' && s[ i + 1 ] == '-' ) { str0.push_back( ' ' ); str0.push_back( '-' ); mark ++; } else if( s[ i ] == '-' && s[ i - 1 ] == '(' );
else
{
if( s[ i ] != ')' )
{
if( s[ i ] == '(' ) { str0.push_back( ' ' ); mystack.push( '(' ); }
else if( priority( s[ i ] ) == -1 || s[ i ] == '.' )//数字和小数点
{
str0.push_back( s[ i ] );
}
else//符号
{
str0.push_back( ' ' ); if( mystack.empty() ) mystack.push( s[ i ] );
else
{
if( priority( s[ i ] ) > priority( mystack.top() ) ) { str0.push_back( ' ' ); mystack.push( s[ i ] ); }
else if( priority( s[ i ] ) == priority( mystack.top() ) ) { str0.push_back( mystack.top() ); mystack.pop(); str0.push_back( ' ' ); mystack.push( s[ i ] ); }
else//优先级小于栈顶元素
{
while( mystack.size() != 0 )
{
if( mystack.top() == '(' ) break;
str0.push_back( ' ' );
str0.push_back( mystack.top() );
mystack.pop();
}
str0.push_back( ' ' );
mystack.push( s[ i ] );
}
}
}
}

else//当s[i]==')'
{
if( mark != 0 )//判断括号前为负数
{
mark--;
}
else
{
while( mystack.top() != '(' )
{
str0.push_back( ' ' );
str0.push_back( mystack.top() );
mystack.pop();
}
mystack.pop();
}
}
}
}
while( mystack.size() != 0 ) { str0.push_back( ' ' ); str0.push_back( mystack.top() ); mystack.pop(); }
return str0;
}

//转化string到int
double exchange( string a )
{
istringstream ss( a );
double num;
ss >> num;
return num;
}

void aadd()
{
ans = container.top();
container.pop();
ans += container.top();
container.pop();
container.push( ans );
}

void mminus()
{
ans = container.top();
container.pop();
ans = container.top() - ans;
container.pop();
container.push( ans );
}

void mmul()
{
ans = container.top();
container.pop();
ans *= container.top();
container.pop();
container.push( ans );
}

void ddiv()
{
ans = container.top();
container.pop();
ans = container.top() / ans;
container.pop();
container.push( ans );
}

void ppow()
{
ans = container.top();
container.pop();
ans = pow( container.top(), ans );
container.pop();
container.push( ans );
}

void ssin()
{
ans = sin( container.top() );
container.pop();
container.push( ans );
}

void ccos()
{
ans = cos( container.top() );
container.pop();
container.push( ans );
}

void ttan()
{
ans = tan( container.top() );
container.pop();
container.push( ans );
}

void llog()
{
ans = log( container.top() );
container.pop();
container.push( ans );
}

void llg()
{
ans = log10( container.top() );
container.pop();
container.push( ans );
}

void aabs()
{
ans = abs( container.top() );
container.pop();
container.push( ans );
}

int main()
{
stack< double > s;
string str;
cout<<endl;
cout<<"———————— This is a culculator ————————"<<endl;

while( cout<<">> " && cin >> str )
{

//cout<<change( str )<<endl;;
//cout << infix_to_uffix( change( str ) ) << endl;

stringstream ss( infix_to_uffix( change( str ) ) );
string sp;
while( ss >> sp )
{
if( int( sp[ 0 ] ) > 47 && int( sp[ 0 ] ) < 58 )
{
container.push( exchange( sp ) );
}
else if( sp[ 0 ] == '-' && sp.size() != 1 )
{
if( int( sp[ 1 ] ) > 47 && int( sp[ 1 ] ) < 58 )
container.push( exchange( sp ) );
else if( sp[ 1 ] == 'p' )
{
double pi = 3.14159265358979323846264;
container.push( -pi );
}
else if( sp[ 1 ] == 'e' )
{
container.push( -2.71828182846 );
}
}
else if( sp[ 0 ] == 'p' )
{
double pi = 3.14159265358979323846264;
container.push( pi );
}
else if( sp[ 0 ] == 'e' )
container.push( 2.71828182846 );
else if( sp[ 0 ] == '+' )
aadd();
else if( sp[ 0 ] == '-' )
mminus();
else if( sp[ 0 ] == '*' )
mmul();
else if( sp[ 0 ] == '/' )
ddiv();
else if( sp[ 0 ] == '^' )
ppow();
else if( sp[ 0 ] == 's' )
ssin();
else if( sp[ 0 ] == 'c' )
ccos();
else if( sp[ 0 ] == 't' )
ttan();
else if( sp[ 0 ] == 'l' )
llg();
else if( sp[ 0 ] == 'o' )
llog();
else if( sp[ 0 ] == 'a' )
aabs();
else;
}
if( container.top() > 0 && container.top() < pow( 10.0,-8 ) || container.top() < 0 && container.top() > 0-pow( 10.0,-8 ) ) cout << "result:" << 0 << endl; else printf("result:%.8g\n",container.top()); cout<<endl;

}
}


三、实验总结

  本次实验虽然大体上实现了计算器的运算,但是不足的地方是在于分割字符串的方法比较粗鲁,导致程序过程不够简单,感觉饶了远路,后面思考可以用vector类实现字符串分割(用于输出运算字符串的过程),理论上讲会更加简便。

计算器用户使用说明

  本计算器计算根据运算符号的优先级以及括号运算,支持负数、小数的运算。

  本计算器能使用的运算有:加法(+)、减法(-)、乘法(*)、除法(/)、幂运算(^)、正弦(sin)、余弦(cos)、正切(tan)、底为10的对数(lg)、底为e的对数(log)、绝对值(abs)。

  打开计算器,显示“——————— This is a calculator ————————”。第二行显示“>>”,即可以在后面输入表达式,输入完毕之后回车即可输出结果,在“result:”之后。精度为小数点后8位。

  输入方法(任何数字与符号之间都不可以输入空格):

A.数据

  1.正数 直接输入

    例如:

    >> 3

    result: 3

  2.小数 直接输入

    例如:

    >> 3.1415

    result: 3.1415

  3.负数 括号(英文括号)输入(若在开头可以直接输入)

    例如:

    >> -3.2

    result: -3.2

    >> 1+(-3.2)

    result: -2.2

  4.其他自然数 直接输入

    >> pi

    result: 3.1415927

    >> e

    result: 2.7182818

B.运算符

  1.加法 输入数字后输入“+”。

    例如:

    >> 1+3

    result: 4

  2.减法 输入数字后输入“-”。

    例如:

    >> 1-3

    result: -2

  3.乘法 输入数字后输入“*”。

    例如:

    >> 10*9

    result: 99

  4.除法 输入数字后输入“/”。

    例如:

    >> 6/7

    result: 0.85714286

  5.幂运算 输入数字后输入“^”。

    例如:

    >> (-5)^3

    result: -125

  6.正弦 输入sin后在括号内输入数据。

    例如:

    >> sin(pi)-1

    result: -1

  7.余弦 输入cos后在括号内输入数据。

    例如:

    >> cos(pi/2)-1

    result: -1

  8.正切 输入tan后在括号内输入数据。

    例如:

    >> tan((-pi)/4)

    result: -1

  9.底为e的对数 输入log后在括号内输入数据。

    例如:

    >> log(e)

    result: 1

  10.底为10的对数 输入lg后在括号内输入数据。

    例如:

    >> lg(10)

    result: 1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 数据结构
相关文章推荐