DAG优化 SDUT 【编译原理】 C++

目录

Description

代码:

分析:


Description

大家都学过了代码优化,其中有一个DAG优化,这次我们就练习这个操作。

Input

输入第一行为一个整数n(n < 100),表示该组输入的表达式的个数之后n行为表达式,每个变量为一个字母,表达式仅包括二元运算 + - * /           

例如:A=B+C

Output

通过构造DAG图,进行代码优化,只需要保留AB,删除无用变量,删除变量时,尽量保留最早出现的变量。PS:保证AB的值不同。

sample

Input 

3
A=B+C
B=B+B
A=C+C

Output

B=B+B
A=C+C

代码:

#include<bits/stdc++.h>
using namespace std;
struct nd {
	char ch;
	vector<char>val;//附加标记,就是图中节点右侧的变量
	int left = -1, right = -1;//指针,使用链式前向星思想
}point[200];
int num = 0;//point数组中已使用的下标,每使用一个num++,即num就是DAG图中的行数
string ans[200];//存放重构语句
bool flag[200];//标记重构的语句

bool find_val(int id, char ch);//查找id节点中val是否存在ch
int add(char ch);//添加叶子节点
void add_op(char op, char ch, int left, int right);//添加运算符节点
char chose_var(int id);//遍历id节点的val中是否有AB
void save(char ch);//依据要保留的字符ch进行删除无用变量
void dfs(int id);//以id为起点进行dfs遍历


int main()
{
	int t;
	string sentence;
	cin >> t;
	for (int i = 0; i < t; i++) {
		cin >> sentence;//输入形如a=b+c的语句
		int left = add(sentence[2]), right = add(sentence[4]);//建立叶子节点
		add_op(sentence[3], sentence[0], left, right);//建立运算符节点
	}
	for (int i = 0; i < num; i++) {//重构代码
		if (point[i].left != -1 && point[i].right != -1) {//如果不是叶子节点,重构一条语句,分析中有解释为什么要在写一个exist函数
			ans[i].push_back(chose_var(i));//对于每一个选取的变量都要判断,下面两个变量也一样
			ans[i].push_back('=');
			ans[i].push_back(chose_var(point[i].left));
			ans[i].push_back(point[i].ch);
			ans[i].push_back(chose_var(point[i].right));
		}
	}
	save('A'), save('B');//因为最后要保留AB所以分别依据A和B进行删除无用变量
	for (int i = 0; i < num; i++)//结果输出
		if (flag[i])
			cout << ans[i] << endl;
	return 0;
}
bool find_val(int id, char ch) {//查找id节点中val是否存在ch
	for (char t : point[id].val)//迭代遍历
		if (t == ch) return true;
	return false;
}
int add(char ch) {//添加叶子节点
	for (int i = 0; i < num; i++)//如果节点存在,有叶子节点或者附加标记中有返回节点号
		if (ch == point[i].ch || find_val(i, ch)) return i;
	point[num].ch = ch;
	return num++;
}
void add_op(char op, char ch, int left, int right) {//添加运算符节点
	for (int i = 0; i < num; i++)//如果有类似运算符节点则在该节点的附加标记中添加变量,如何是类似的分析中解释
		if (point[i].ch == op && point[i].left == left && point[i].right == right) {
			point[i].val.push_back(ch); return;
		}
	point[num].ch = op;//建立新的运算符节点
	point[num].val.push_back(ch);
	point[num].left = left;
	point[num].right = right;
	num++;
}
char chose_var(int id) {
	/*该函数是综合了两种功能,在重构代码是等号两边的变量都能用,不需要在左右子树中写一长串判断了*/
	if (!point[id].val.size()) return point[id].ch;
	for (char tt : point[id].val)
		if (tt == 'A' || tt == 'B') return tt;
	return point[id].val[0];
}
void save(char ch) {
	for (int i = num - 1; i >= 0; i--) {//删除无用语句,在分析中有详细解释
		if (ans[i][0] == ch) {
			dfs(i);  return;
		}
	}
}
void dfs(int id) {//以id为起点进行dfs遍历
	if (point[id].left != -1 && point[id].right != -1)
	{
		flag[id] = true;
		dfs(point[id].left);
		dfs(point[id].right);
	}
}

代码总体结构:main函数中共4个for循环,实现分析中构建DAG图、重构代码、删除无用变量三部分。

1.构建DAG图:第一个for循环,以及add()、add_op()、find_val()三个函数。

add()函数用来添加等号右侧变量的叶子节点,add_op()用来添加运算符和等号左侧变量,find_val()用来查找是否存在类似节点(分析中有解释)。

2.重构代码:第二个for循环,以及chose_var()一个函数。

chose_var()用来判断重构代码是应选取那个变量。

3.删除无用变量:save(), dfs()两个个函数。

save()为给定一个要保留的字符,可以保留与该字符有关的语句。

4.最后一个for循环为结果输出。

分析:

        首先要知道DAG图优化的过程,大佬对DAG图优化的讲解。这里主要看2. 基本块内的局部优化,可以只对这部分进行呢研读。

         虽然有大佬的讲解,但这我还是描述一下优化的过程,代码便是基于这里的描述过程编写的。阅读下面之前首先要学会上课老师将的构建DAG图、代码重构、去除无用变量的过程学会。

        1. 构建DAG图,下面是构架的过程,灵魂画手凑合看。这里使用的图的存储方式是链式前向星思想。

        说明:每个节点使用结构体数组进行存储,这里的0,1,2,3,4就是数组下标,结构体内容如下:ch为圆圈里的内容;val为附加标记,存放圆圈右边的AB等变量;left和right为指针,指向子节点的下标;。

struct nd {
	char ch;
	vector<char> val;
    int left = -1, right = -1;
}point[110];

        第一行输入A=B+C,建立过程:建立第0个节点B和第1个节点C,然后建立第2个节点,left和right分别指向节点0,1;ch=‘=’;val = ‘A’。后面类似建立,只是每次建立节点前先扫描前面节点看看是否已存在类似节点。

        这里所说的类似节点有两种情况:假设输入的是a=b+c  对于等号右边的b,c就是直接建立一个叶子节点,所以只需要找一下叶子节点以及附加标记中有没有b,c,没有就新建一个叶子节点,有就不建立;对于等号左边的a,它的类似节点是左右子树相同运算符也相同的节点,没有就新建,有就在附加标记val后面追加a.

        2.重构代码,这个是最简单的一步,只需要将上面构造出来的DAG遍历一遍,如果不是叶子节点(tt[i].left != -1 && tt[i].right != -1)就构造一条语句如:a=b+c。“删除变量时,尽量保留最早出现的变量。所以在构造语句时应该直接取附加标记里的第一个变量作为等号左侧的a即可,附加标记中第一个就是最早出现的,但是但是!!!这里增加了一组测试数据,就是当输入数据形如:b=C+d
A=C+d

两条时我们就不能取附加标记里的第一个了,而是取第二个(这里应该都理解吧!最后要保留AB),所以在取变量时需要遍历附加标记如果有A或B,有要取AB。但是这里的测试数据还是不充分,当出现A=a+b B=a+b时,测试数句没有充分测试,这里应该输出A,因为A出现的早,但其实无论是输出A还是B都能过。

        3.删除无用变量,这一步就是要将和AB计算无关的变量进行删除,计算的中间变量已经在重构代码是清掉了。建立的DAG图其实是一个森林(由多棵树组成的,这里说的树是二叉树一种数据结构,不是植树节值得树),这里只需要找到a=b+c中a=A或者a=B的语句,然后以此节点作为根进行二叉树的遍历找出所有子树并进行标记,这要就可以把所以与AB计算有关的语句标记出来。最后将重构后的语句中有标记的输出即可。

  • 12
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

碧羽o(* ̄▽ ̄*)ブ回雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值