编译原理--简单计算器(递归、堆栈的左结合、右结合),随机算式生成

1.1:左结合文法:

exp -> exp addop term | term

addop -> + | -

term -> term mulop factor | factor

mulop -> * | /

factor -> (exp) | number

1.2左结合代码实现

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
char token; /* global token variable */
/*function prototype for recursive calls*/
float exp(void);
float term(void);
float factor(void);
int i = 0;
string Equation;
char getc(){
    return Equation[i++];
}
int getnum(){
    if(isdigit(Equation[i]) && isdigit(Equation[i+1])){
        i+=2;
        return (Equation[i-3]-'0')*100+(Equation[i-2]-'0')*10+Equation[i-1]-'0';
    } 
    if(isdigit(Equation[i])){
        i++;
        return (Equation[i-2]-'0')*10+Equation[i-1]-'0';
    }
    return Equation[i-1]-'0';
}
void error(void)
{
    fprintf(stderr, "error\n");
    exit(1);
}
void Div0error(void)
{
    fprintf(stderr, "Divide by Zero Error\n");
    exit(1);
}
void match(char expectedToken)
{
    if (token==expectedToken) token=getc();
    else error();
}

int main()
{
    float result;
    /*load token with first character for lookahead*/
    Equation="1-2+3";//这里是要计算的算式
    token=getc();
    result=exp();
    if (token=='\0') /*check for end of line*/
    printf("left-associative Result = %.2f\n", result);
    else error(); /*extraneous chars on line*/
    return 0;
}
float exp(void)
{
    float temp=term();
    while ( (token=='+') || (token=='-') )
    switch (token) 
    {
        case '+': match ('+'); temp+=term(); break;
        case '-': match ('-'); temp-=term(); break;
    }
    return temp;
}
float term(void)
{
    float temp=factor();
    while (token=='*' || token == '/')
    switch(token)
    {
        case '*': match('*');temp*=factor();break;
        case '/': 
            match('/');
            float divisor = factor();
            if(divisor == 0)
                Div0error();
            else 
                temp/=divisor;
            break;
    }
    return temp;
}
float factor(void)
{
    float temp;
    if (token=='(') 
    {
        match ('(');
        temp = exp();
        match(')');
    }
    else
    if (isdigit(token)) 
    {
        temp = getnum();
        token = getc();
    }
    else error();
    return temp;
}

2.1右结合文法

exp ->  term [addop exp]

addop -> + | -

term -> factor [mulop term]

mulop -> * | /

factor -> (exp) | number

2.2右结合代码实现

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
char token; /* global token variable */
/*function prototype for recursive calls*/
float exp(void);
float term(void);
float factor(void);
int i = 0;
string Equation;
char getc(){
    return Equation[i++];
}
int getnum(){
    if(isdigit(Equation[i]) && isdigit(Equation[i+1])){
        i+=2;
        return (Equation[i-3]-'0')*100+(Equation[i-2]-'0')*10+Equation[i-1]-'0';
    } 
    if(isdigit(Equation[i])){
        i++;
        return (Equation[i-2]-'0')*10+Equation[i-1]-'0';
    }
    return Equation[i-1]-'0';
}
void error(void)
{
    fprintf(stderr, "error\n");
    exit(1);
}
void Div0error(void)
{
    fprintf(stderr, "Divide by Zero Error\n");
    exit(1);
}
void match(char expectedToken)
{
    if (token==expectedToken) token=getc();
    else error();
}
int main()
{
    float result;
    /*load token with first character for lookahead*/
    Equation="1-2+3";//这里是要计算的算式
    token=getc(); 
    result=exp();
    if (token=='\0') /*check for end of line*/
    printf("right-associative Result = %.2f\n", result);
    else error(); /*extraneous chars on line*/
    return 0;
}
float exp(void)
{
    float temp=term();
    while ( (token=='+') || (token=='-') )
    switch (token) 
    {
        case '+': match ('+'); temp+=exp(); break;
        case '-': match ('-'); temp-=exp(); break;
    }
    return temp;
}
float term(void)
{
    float temp=factor();
    while (token=='*' || token == '/')
    switch(token)
    {
        case '*': match('*');temp*=term();break;
        case '/': 
            match('/');
            float divisor = term();
            if(divisor != 0)
                temp/=divisor; 
            else Div0error();
            break;
    }
    return temp;
}
float factor(void)
{
    float temp;
    if (token=='(') 
    {
        match ('(');
        temp = exp();
        match(')');
    }
    else
    if (isdigit(token)) 
    {
        temp = getnum();
        token = getc();
    }
    else error();
    return temp;
}

3.1堆栈的左右结合实现

中缀表达->后缀表达式

左右结合分别的关键在于同级运算的执行先后顺序,在堆栈实现中即是要修改栈顶比较时的判断

3.1.1堆栈左结合

#include <iostream>
#include <stack>
using namespace std;
string Equation="1-2+3";//要计算的算式
string retL[100];
int posL =0;
char getc(){
    return Equation[posL++];
}
int getnum(){
    if(isdigit(Equation[posL]) && isdigit(Equation[posL+1])){
        posL+=2;
        return (Equation[posL-3]-'0')*100+(Equation[posL-2]-'0')*10+Equation[posL-1]-'0';
    } 
    if(isdigit(Equation[posL])){
        posL++;
        return (Equation[posL-2]-'0')*10+Equation[posL-1]-'0';
    }
    return Equation[posL-1]-'0';
}
int getPriority(char c){
    if(c == '(') return 1;
    else if(c == '+' || c == '-') return 2;
    else if(c == '*' || c == '/') return 3;
    else if(c == ')') return 4;
    else {
        cout << "error input in getPriority()" << endl;
        return -1;
    }
}
void TonibolanL(int &l){
    char c ;
    int pos = l;
    stack<char> operate;
    c = getc();
    while(1){
        if(c >= '0' && c <= '9'){
            retL[pos++] = to_string(getnum());
        }
        else{
            if(c == '('){
                operate.push(c);
            }else if(c == ')'){
                while(operate.top() != '('){
                    retL[pos++] = operate.top();
                    operate.pop();
                }
                if(operate.top() == '('){
                    operate.pop();
                }
            }
            else if(c == '+' || c == '-' || c == '*' || c == '/'){
                if(operate.empty() || getPriority(c) > getPriority(operate.top())){
                    operate.push(c);
                }
                else {
                    while(getPriority(c) <= getPriority(operate.top())){
                        retL[pos++] = operate.top();
                        operate.pop();
                        if(operate.empty()) break;
                    }
                    operate.push(c);
                }
            }else if(c == '\0' || c == '\n' || c == '='){
                break;
            }else{
                cout << "Error Input:" << c << endl;
                break;
            }
        } 
        c = getc();
    }
    while (!operate.empty())
    {
        retL[pos++] = operate.top();
        operate.pop();
    }
    l = pos;
}
float Calcval(string* ret,int l)
{
	stack<string> st;
	//遍历后缀表达式
    int i = 0;
	while (i<l)  
	{
		if(ret[i] == "+")
		{
			//遇到操作符,连续出栈两个元素,进行运算,在将结果入栈
			float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            float c = b+a;
            st.push(to_string(c));
		}else if(ret[i] == "-"){
            float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            float c = b-a;
            st.push(to_string(c));
        }else if(ret[i] == "*"){
            float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            float c = b*a;
            st.push(to_string(c));
        }else if(ret[i] == "/"){
            float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            if(a!=0){
                float c = b/a;
                st.push(to_string(c));
            }else{
                cout << "Divide by Zero Error" << endl;
                break;
            }
        }
        else if(stof(ret[i])<=100 && stof(ret[i]) >= 0)  //若果是数字直接入栈
		{
			st.push(ret[i]);
		}else{
            cout << "error token:" << ret[i] << endl;
            break;
        }
		i++;
	}
	return stof(st.top());     //栈里面的数据就是最后的结果
}

int main(){
    int l = 0;
    TonibolanL(l);
    // for(int i = 0;i < l;i++){
    //     cout << ret[i] << " ";
    // }
    // cout << endl;
    cout << Calcval(retL,l) << endl;
    return 0;
}

3.1.2堆栈右结合

#include <iostream>
#include <stack>
using namespace std;
string Equation="1-2+3";//这里是要计算的算式
string retR[100];
int posR =0;
char getc(){
    return Equation[posR++];
}
int getnum(){
    if(isdigit(Equation[posR]) && isdigit(Equation[posR+1])){
        posR+=2;
        return (Equation[posR-3]-'0')*100+(Equation[posR-2]-'0')*10+Equation[posR-1]-'0';
    } 
    if(isdigit(Equation[posR])){
        posR++;
        return (Equation[posR-2]-'0')*10+Equation[posR-1]-'0';
    }
    return Equation[posR-1]-'0';
}
int getPriority(char c){
    if(c == '(') return 1;
    else if(c == '+' || c == '-') return 2;
    else if(c == '*' || c == '/') return 3;
    else if(c == ')') return 4;
    else {
        cout << "error input in getPriority()" << endl;
        return -1;
    }
}
void TonibolanR(int &r){
    char c ;
    int pos = r;
    stack<char> operate;
    c = getc();
    while(1){
        if(c >= '0' && c <= '9'){
            retR[pos++] = to_string(getnum());
        }
        else{
            if(c == '('){
                operate.push(c);
            }else if(c == ')'){
                while(operate.top() != '('){
                    retR[pos++] = operate.top();
                    operate.pop();
                }
                if(operate.top() == '('){
                    operate.pop();
                }
            }
            else if(c == '+' || c == '-' || c == '*' || c == '/'){
                //右结合只需要把这里的优先级比较改掉即可
                if(operate.empty() || getPriority(c) >= getPriority(operate.top())){
                    operate.push(c);
                }
                else {
                    while(getPriority(c) < getPriority(operate.top())){
                        retR[pos++] = operate.top();
                        operate.pop();
                        if(operate.empty()) break;
                    }
                    operate.push(c);
                }
            }else if(c == '\0' || c == '\n' || c == '='){
                break;
            }else{
                cout << "Error Input:" << c << endl;
                break;
            }
        } 
        c = getc();
    }
    while (!operate.empty())
    {
        retR[pos++] = operate.top();
        operate.pop();
    }
    r = pos;
}
float Calcval(string* ret,int l)
{
	stack<string> st;
	//遍历后缀表达式
    int i = 0;
	while (i<l)  
	{
		if(ret[i] == "+")
		{
			//遇到操作符,连续出栈两个元素,进行运算,在将结果入栈
			float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            float c = b+a;
            st.push(to_string(c));
		}else if(ret[i] == "-"){
            float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            float c = b-a;
            st.push(to_string(c));
        }else if(ret[i] == "*"){
            float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            float c = b*a;
            st.push(to_string(c));
        }else if(ret[i] == "/"){
            float a = stof(st.top());
            st.pop();
            float b = stof(st.top());
            st.pop();
            if(a!=0){
                st.push(to_string(b/a));
            }else{
                cout << "Divide by Zero Error" << endl;
                break;
            }
        }
        else if(stof(ret[i])<=100 && stof(ret[i]) >= 0)  //若果是数字直接入栈
		{
			st.push(ret[i]);
		}else{
            cout << "error token:" << ret[i] << endl;
            break;
        }
		i++;
	}
	return stof(st.top());     //栈里面的数据就是最后的结果
}
int main(){
    int r = 0;
    TonibolanR(r);
    for(int i = 0;i < r;i++){
        cout << retR[i] << " ";
    }
    cout << endl;
    cout << Calcval(retR,r) << endl;
    return 0;
}

4.1随机算式的生成

这里目标是生成绝对准确的算式

1、生成[3,5]个运算分量,范围在[0,100]

2、生成[2,4]个运算符,[+,-,*,/]随机

3、50%概率生成括号

4、先生成左括号(只能在运算分量之前)

5、在根据左括号位置确定右括号(只能在数字之后,且左右括号之间至少有两个运算分量和一个运算符)

#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
using namespace std;
string op[4] = {"+","-","*","/"};
//获取运算分量
string getRandomNum(){
    return to_string(rand() % 101); //0、1、2、3......99、100
}
//获取运算符
int getRandomOp(){
     return rand() % 4; //0、1、2、3
}
//获取等式中运算分量个数
int getRandomEquationLength(){
    return rand() % 3 + 3;//运算分量数可能为3、4、5 ,运算符数量则可能是2、3、4
}
//是否出现括号
int ifBracket(){
    return rand() % 2;//0、1
}
//左括号出现的位置
int LeftBracket_pos(int length){
    switch(length){
        case 3: return rand()%2;break;
        case 4: return rand()%3;break;
        case 5: return rand()%4;break;
        default: return -1;break;
    }
}
//右括号出现的位置
int RightBracket_pos(int length,int l_pos){
    switch(length){
        case 3: 
            switch(l_pos){
                case 0: return rand()%2 + 1;break;
                case 1: return 2;break;
            }
            break;
        case 4:
            switch(l_pos){
                case 0: return rand()%3 + 1;break;
                case 1: return rand()%2 + 2;break;
                case 2: return 3;break;
            }
            break;
        case 5: 
            switch(l_pos){
                case 0: return rand()%4 + 1;break;
                case 1: return rand()%3 + 2;break;
                case 2: return rand()%2 + 3;break;
                case 3: return 4;break;
            }
            break;
        default: return -1;break;
    }
    return -1;
}
string getRandomEquation(){
    string equation = "";
    int length = getRandomEquationLength();
    if(ifBracket()){
        int l_pos = LeftBracket_pos(length);
        int r_pos = RightBracket_pos(length,l_pos);
            for(int i = 0;i < length;i++){
            //(
            if(i == l_pos) equation += "(";
            equation += getRandomNum();
            //)
            if(i == r_pos) equation += ")";
            if(i != length - 1) equation += op[getRandomOp()];
        }
    }else{
        for(int i = 0;i < length;i++){
            equation += getRandomNum();
            if(i != length - 1) equation += op[getRandomOp()];
        }
    }
   
    return equation;
}
int main(){
    srand((unsigned int)time(NULL));
    for(int i=0;i<100;i++){
        cout << getRandomEquation() <<endl;
    }
    return 0;
}

5.1引用参考博客

堆栈实现计算器(9条消息) 数据结构--简单实现计算器_数据结构 简单计算器_哈哈哈哈哈哈丶的博客-CSDN博客

随机数的生成

(9条消息) C语言中生成随机数_c语言随机数生成1到100_懒癌晚期524的博客-CSDN博客 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值