机试刷题:模拟编译系统(字符串处理,栈,计算器)

一、前言

        知识点:字符串处理,栈和向量的使用,计算器的实现

二、题目

        该程序会模拟一个解释运行的编译系统,每次从标准输入中读入一行指令,都要进行对应的操作,该程序中存在四种语句,语句最长200个字,每个语句古且仅古一行,每行最多一个语句。

        四种语句分别如下:
        1.输入语句,格式为"read+空格+<变量序列>+换行符",变量序列是以空格分隔的变量名称的组合,变量的名字只可能为单个的小写字母,变量不需要提前声明,紧接着下一行,输入若干十进制整数),每个整数和上一行的变量对应赋值
        2.赋值语句、格式为"变量+等于符号+表达式+换行符",表达式中包含十进制整数,变量,+-*/()这六种计算符号,整个语句不包含空白符
        3.输出语句,格式为"print+空格+<变量序列>+换行符",紧接着下一行,输出若干个变量的值,值以浮点数形式输出,保留两位小数,值之间以空格分隔最后一个值后面不跟空格跟换行符
        4.结束语句,格式为"exit+换行符",该语句后直接结束程序运行
        

        测试样例中,所有语句均不包含语法错误,所有变量在使用前均会赋值(不需要考虑输入错误的情况)


输入与输出:

read a

10

b=20

c=(a+b)/4

print a b c

10.00 20.00 7.50

Exit

三、分析

        首先需要对输入的字符串进行接收和处理,根据不同的关键字进行不同的处理,考虑分别定义相关函数进行调用,read、print、exit、expression相关功能的四个函数。其中,对read和print语句,一开始考虑整行读入,那么便需要按照空格分割字符串,后来改用了先读入一个字符串单词的方式,可以直接先行判断关键字。

        此外,难点在于计算器的实现。

        计算器的算法思想如下:

设立一个操作符栈op和一个操作数栈num。 op栈底放入结束符#。

• 从左到右扫描表达式,若为操作数则压入num栈,继续扫描;

  若为操作符θ2,则将其与op栈顶元素θ1比较:

        – 若θ2=θ1=‘#’ ,则运算结束,结果在opnd栈顶;

        – 若θ2的优先级高,则θ2入op栈,继续扫描;

        – 若优先级相等,则退出optr栈顶元素‘(’,继续扫描;

        – 若θ2的优先级低,则从optr栈退出θ1 ,从opnd栈退出两个操作数b和a 作运算a θ1 b后将结果压入num栈。

PS: 表达式expression需要以“#”结尾,与前面op栈放入的结束符#呼应,来判断运算结束。此外, θ2为在表达式中,处于右边的运算符;θ1为处于左边的运算符。

        计算器的算符优先级如下表:

        

        根据四则运算法则,写出上表。并数字化表格,左>右,则为-1; 左 < 右,则为1;相等则为0;结束为2;空则非法,为-2。

四、代码实现

#include <iostream>
#include <algorithm>
#include <stack>
#include <string>
#include <vector>
#include <iomanip>

using namespace std;

double value[26];   // 存储变量值

void read_do();
void print_do();
void express(string expr);
double compute(vector<string> expression);
int get_level(string lop, string rop);
int get_op_num(string op);

int main() {
	string str;
	while (true) {
		cin >> str;
		if (str == "Exit") {
			break;
		}
		else if (str == "read") {
			read_do();
		}
		else if (str == "print") {
			print_do();
		}
		else {
			express(str);
		}
	}

	return 0;
}

void read_do() {
	string str_r;
	int tmp;

	getline(cin, str_r);

	for (int i = 0; i < str_r.size(); i++) {
		if (isalpha(str_r[i])) {
			cin >> tmp;
			value[str_r[i] - 'a'] = tmp;
		}
	}
}

void print_do() {
	string str_p;

	getline(cin, str_p);
	for (int i = 0; i < str_p.size(); i++) {
		if (isalpha(str_p[i])) {
			cout << fixed << setprecision(2) << value[str_p[i] - 'a'];  // 格式控制
			if (i == (str_p.size() - 1)) cout << endl;
			else cout << " ";
		}
	}
}
void express(string expr) {
	vector<string> expression;

	for (int i = 0; i < expr.length(); i++) {
		string num = "";

		int j = 0;
		bool flag = false; // 二位以上连续数字为true;
		while (isdigit(expr[i + j])) {
			num += expr[i + j];
			j++;
		}
		if (j > 1) {
			expression.push_back(num);
			i = i + j;
		}
		else {
			string s = "";
			expression.push_back( s+=expr[i]);
		}
			
	}

	compute(expression);
}

double compute(vector<string> expression) {
	// 如果是"b=3"这种长度为3的简单式,直接赋值
	if (expression.size() == 3) {
		value[expression[0][0] - 'a'] = stoi(expression[2]);  //expression[0][0]字符串转字符
		return 0;
	}


	stack<double> num;  // 操作数栈
	stack<string> op;	// 操作符栈
	//num.push("#");
	//

	double result = 0;
	expression.push_back("#");
	op.push("#");
	for (int i = 2; i < expression.size(); i++) {
		// 若为变量,则获取其值,压入操作数栈中
		if (islower(expression[i][0])) {
			num.push(value[expression[i][0] - 'a']);
		}
		// 若为数字,压入操作数栈
		else if (isdigit(expression[i][0])) {
			num.push(stoi(expression[i]));
		}
		// 操作符, 右
		else {
			string right_op = expression[i];
			string left_op = op.top();
			int level = get_level(left_op, right_op);
			switch (level) {
			case 2: // 运算结束
				result = num.top();
				break;
			case 1: // 右运算符优先级高,压入操作符栈
				op.push(right_op);
				break;
			case 0: // 优先级相等,弹出op栈顶的”(”
				op.pop();
				break;
			case -1: { // 右运算符优先级低,弹出左运算符和两个操作数进行运算
				op.pop();
				double num_b, num_a;
				num_b = num.top();
				num.pop();
				num_a = num.top();
				num.pop();

				double calculate = 0;  // 存一次运算的结果
				if (left_op == "+") {
					calculate = num_a + num_b;
				}
				else if (left_op == "-") {
					calculate = num_a - num_b;
				}
				else if (left_op == "*") {
					calculate = num_a * num_b;
				}
				else if (left_op == "/") {
					calculate = num_a / num_b;
				}

				num.push(calculate);

				break;
			}
			case -2:
				break;
			}
		}
	}

	value[expression[0][0] - 'a'] = num.top();
	return 0;

}

// 比较算符优先级,若右算符优先级高,则返回1; 相等返回0; 右算符优先级低,则返回-1;非法-2;结束2
int get_level(string lop, string rop) {
	//int table[7][7];
	int level[49] = { -1,-1,1,1,1,-1,-1,
			-1,-1,1,1,1,-1,-1,
		-1,-1,-1,-1,1,-1,-1,
		-1,-1,-1,-1,1,-1,-1,
		1,1,1,1,1,0,-2,
		-1,-1,-1,-1,-2,-1,-1,
		1,1,1,1,1,-2,2
	};
	int lopNum = get_op_num(lop);
	int ropNum = get_op_num(rop);
	return level[lopNum * 7 + ropNum];
}

int get_op_num(string op) {
	if (op == "+")  return 0;
	else if (op == "-") return 1;
	else if (op == "*") return 2;
	else if (op == "/") return 3;
	else if (op == "(") return 4;
	else if (op == ")") return 5;
	else if (op == "#") return 6;
	return -1;  // 错误-1
}

五、总结

1. cctype库有函数isdigit判断字符是否数字,isalpha判断字符是否字母,islower判断是否小写,还有更多函数。

2. 输出格式控制,保持两位小数点。

cout << fixed << setprecision(2) << value[str_p[i] - 'a'];  // 格式控制

3. 输入利用cin,自动按空格、换行分割,读出的不再留缓冲,而没读出的,包括空格,会留在输入缓冲中。

测试结果如图:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值