日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【C++实现】编译原理 免考小队 消除一切左递归

發布時間:2023/12/8 c/c++ 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C++实现】编译原理 免考小队 消除一切左递归 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景

期末考試免考,沖!

實驗名稱

消除一切左遞歸

實驗時間

2020年5月27日 到 2020年5月31日

院系

信息科學與工程學院

組員姓名

Chocolate、kry2025、鐘先生、leo、小光

實驗環境介紹

  • windows 10 操作系統
  • Eclipse 進行 java 編程
  • CodeBlocks 進行 C++ 編程

實驗目的與要求

目的

  • 深刻理解左遞歸的算法
  • 掌握消除左遞歸的過程
  • 加強團隊合作能力
  • 提高自身的編程能力和解決問題的能力

要求

  • 編程實現消除一切左遞歸
  • 算法簡潔,不冗余

解決問題

產生式直接消除左遞歸

形如 P → Pα | β 可以通過直接消除轉化為:

P → βP′ P′ → αP′ | ?

產生式間接消除左遞歸

有時候雖然形式上產生式沒有遞歸,但是因為形成了環,所以導致進行閉包運算后出現左遞歸,如下:

S → Qc | c Q → Rb | b R → Sa | a

雖不具有左遞歸,但S、Q、R都是左遞歸的,因為經過若干次推導有

  • SQcRbcSabc
  • QRbSabQcab
  • RSaQcaRbca

就顯現出其左遞歸性了,這就是間接左遞歸文法。

消除間接左遞歸的方法是:

把間接左遞歸文法改寫為直接左遞歸文法,然后用消除直接左遞歸的方法改寫文法。
如果一個文法不含有回路,即形如PP的推導,也不含有以ε為右部的產生式,那么就可以采用下述算法消除文法的所有左遞歸。

for (i=1;i<=n;i++for (j=1;j<=i-1;j++{ 把形如Ai→Ajγ的產生式改寫成Ai→δ1γ /δ2γ //δkγ 其中Aj→δ1 /δ2 //δk是關于的Aj全部規則;消除Ai規則中的直接左遞歸;}

實驗結果

源代碼

#include<bits/stdc++.h> #define endl '\n' using namespace std; const int maxn=100+5; char buf[maxn]; //輸入產生式 int n; //產生式的數量 class node{ public:string left; //產生式左部set<string> right; //產生式右部node(const string& str){left=str;right.clear();}void push(const string& str){right.insert(str);}void print(){printf("%s->",left.c_str());set<string>::iterator it = right.begin();printf("%s",it->c_str());it++;for (;it!= right.end();it++ )printf("|%s",it->c_str());cout<<endl;} }; map<string,int> mp; //記錄每個node的下標 vector<node> vnode; //每一個產生式 string start; //文法G[s] bool used[maxn]; //用于去掉無用產生式 //初始化工作 void init(){mp.clear();vnode.clear();start="S"; } //消除間接左遞歸 void eliminateIndirectLeftRecursion(){for(int i=0;i<vnode.size();i++){for(int j=0;j<i;j++){vector<string> ans;set<string>& righti=vnode[i].right;set<string>& rightj=vnode[j].right;char ch=vnode[j].left[0]; //取所有Aj產生式的左部的非終結符set<string>::iterator iti,itj;for(iti=righti.begin();iti!=righti.end();iti++){if(iti->at(0)==ch) //如果當前產生式右部的非終結符和Aj相同for(itj=rightj.begin();itj!=rightj.end();itj++)ans.push_back(*itj+iti->substr(1)); //進行替換操作,先存儲起來}while(!righti.empty()){if(righti.begin()->at(0)!=ch) //存儲當前沒有替換的產生式右部ans.push_back(*righti.begin());righti.erase(righti.begin()); //被替換過的產生式右部也刪除掉}for(int k=0;k<ans.size();k++) //將替換過的產生式右部進行更新操作righti.insert(ans[k]);}}cout<<"消除間接左遞歸后的結果:"<<endl;for(int k=0;k<vnode.size();k++)vnode[k].print();cout<<endl; } //消除直接左遞歸 void eliminateDirectLeftRecursion(){for(int i=0;i<vnode.size();i++){char ch=vnode[i].left[0];set<string>& right=vnode[i].right; //拿到當前右部set<string>::iterator it;string tmp=vnode[i].left.substr(0,1)+"\'"; //對非終結符更改bool flag=true;for(it=right.begin();it!=right.end();it++){if(it->at(0)==ch){vnode.push_back(node(tmp));mp[tmp]=vnode.size();flag=false;break;}}int idx=mp[tmp]-1;if(flag) continue; //對于非終結符不相同的產生式我們需要跳過vector<string> ans;set<string>& tmpSet=vnode[idx].right;tmpSet.insert("~"); //添加空字符while(!right.empty()){if(right.begin()->at(0)==ch)tmpSet.insert(right.begin()->substr(1)+tmp);elseans.push_back(right.begin()->substr(0)+tmp);right.erase(right.begin()); //刪除掉原本產生式右部}for(int k=0;k<ans.size();k++)right.insert(ans[k]); //更新加入新的產生式右部}cout<<endl;cout<<"消除直接左遞歸后的結果:"<<endl;for(int k=0;k<vnode.size();k++)vnode[k].print();cout<<endl; } //搜索 void dfs(int x){if(used[x]) return;used[x]=1; //將當前下標記錄set<string>::iterator it=vnode[x].right.begin();for(;it!=vnode[x].right.end();it++){for(int i=0;i<it->length();i++){if(isupper(it->at(i))){ //判斷是否是大寫字母if(i+1<it->length() && it->at(i+1)=='\'') //如果當前是替換的那個字符dfs(mp[it->substr(i,2)]-1);elsedfs(mp[it->substr(i,1)]-1);}}} } //去掉無用產生式 void removeUselessProduction(){memset(used,0,sizeof(used));int idx=mp[start]-1;dfs(idx); //搜索cout<<"最終文法:"<<endl;vector<node> res;for(int i=0;i<vnode.size();i++)if(used[i]) //存儲已經標記過的產生式res.push_back(vnode[i]);vnode.clear();vnode=res; } int main(){cout<<"請輸入文法G[S]的產生式數量:"<<endl;while(cin>>n){init(); //初始化getchar();cout<<"依次輸入文法G[S]的產生式:"<<endl;for(int i=0;i<n;i++){scanf("%s",buf); //輸入產生式int len=strlen(buf),j;for(j=0;j<len;j++)if(buf[j]=='-'){buf[j]=0; //進行左部和右部切分break;}string tmp=buf; //拿到產生式的左部if(!mp[buf]){vnode.push_back(node(tmp));mp[tmp]=vnode.size();}int idx=mp[tmp]-1; //獲取左部的下標tmp=buf+j+2; //拿到產生式的右部vnode[idx].push(tmp);}//確定開始節點int idx=vnode.size()-1;start=vnode[idx].left[0];eliminateIndirectLeftRecursion(); //消除間接左遞歸eliminateDirectLeftRecursion(); //消除直接左遞歸removeUselessProduction(); //去掉無用產生式/**test*//*cout<<"---------test---------"<<endl;for(int k=0;k<vnode.size();k++)vnode[k].print();*/for(int k=0;k<vnode.size();k++)vnode[k].print();}return 0; }

輸出結果

測試樣例

6 S->Qc S->c Q->Rb Q->b R->Sa R->a6 R->Sa R->a Q->Rb Q->b S->Qc S->c6 Q->Rb Q->b R->Sa R->a S->Qc S->c6 Q->Rb R->Sa Q->b S->Qc R->a S->c

參考文獻

感謝以下博主的文章,本文參考了部分代碼和知識。

馮強計算機考研:編譯原理-消除左遞歸

黎辰:編譯原理(三) 消除文法的左遞歸

學如逆水行舟,不進則退

總結

以上是生活随笔為你收集整理的【C++实现】编译原理 免考小队 消除一切左递归的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。