2.**基礎
總結聯絡
這學期的編譯原理課需要從正規表示式轉為nfa,但是對thompson演算法如何實現很是費解,對於其他部落格的**理解的很困難。於是我開始嘗試利用棧直接對正規表示式進行掃瞄檢索,通過冗餘解決括號的匹配,實現了乙個比較簡陋的正規表示式轉nfa,於是寫這個部落格記錄一下。
首先講一下預設條件。我們假設輸入的正規表示式是一串僅由大小寫字母、數字、『(』、『)』、『×』、『|』組成的字串,預設輸入的字串是左右括號匹配的,且符合正規表示式的構造規則。
然後是基本思路,主要是分治的思想,我們對括號分層處理(具體操作我們後續講),而對於每乙個基礎運算符號(『』、『|』),我們都將其轉換為雙目運算(同上),同時我們要求運算的運算體必須要用()包起來。
對於每一層括號深度,我們都有乙個node棧儲存中間節點。同時應該有乙個or棧儲存或運算的節點(為什麼這麼做我們後面說)。
對於第n+1層和第n層的連線,我們在第n+1層的『)』符號處進行處理。
在開始,我們在第0層的node棧中壓入開始符號『s』。
對正規表示式的當前字元我們記為temp_char。
對於非運算子,我們將當前括號深度的node棧的棧頂取出放到node0,並產生新node1,產生邊node0->node1的邊,這條邊的權為temp_char。同時當當前深度的node棧的大小大於2時pop掉中間節點,僅留下當前深度的頭節點和尾節點。
示例:正規表示式:abc
0.開始符號 s 壓入node[0]
node[2]
node[1]
node[0] s
1.temp_char=『a』 產生新符號a,產生新邊s->a:a
node[2]
node[1]
node[0] s a
2.temp_char=『b』 彈出a 產生新符號b,產生新邊s->b:b
node[2]
node[1]
node[0] s b
3.temp_char=『c』 彈出b 產生新符號c,產生新邊b->c:c
node[2]
node[1]
node[0] s c
*運算由於我們要求了其閉包體必須在()中,因此在)中處理
當檢測到左括號,括號深度+1,並在+1後的括號深度的node棧中壓入乙個新的符號。
示例:正規表示式:a(b)
0.開始符號 s 壓入node[0]
node[2]
node[1]
node[0] s
1.temp_char=『a』 產生新符號a,產生新邊s->a:a
node[2]
node[1]
node[0] s a
2.temp_char=』(』 括號深度+1 產生新符號b
node[2]
node[1] b
node[0] s a
3.temp_char=『b』 產生新符號c,產生新邊b->c:b
node[2]
node[1] b c
node[0] s a
對『)』的處理,我們最後再講。
當檢測到『|』符號,我們首先將當前深度的node棧的棧頂壓入當前深度的or棧中,然後將其從node棧中pop出來,然後將棧頂放到node0中,然後產生乙個新符號放到node1中,產生乙個新邊node0->node1,權為ε (如果是在第0層這樣就結束了),(如果是括號內的)然後在對『)』處理時將那時的當前深度的node棧棧頂和每乙個or棧中的符號產生一條邊,權為ε 。
示例(不包含『)』):
正規表示式:a|b|c
0.開始符號 s 壓入node[0]
node[2]
node[1]
node[0] s
1.temp_char=『a』 產生新符號a,產生新邊s->a:a
node[2]
node[1]
node[0] s a
2.temp_char=』|』 產生新符號b,產生新邊s->b:ε
node[2] or[2]
node[1] or[1]
node[0] s b or[0] a
3.temp_char=『b』 產生新符號c,產生新邊b->c:b
node[2] or[2]
node[1] or[1]
node[0] s c or[0] a
4.temp_char=』|』 產生新符號d,產生新邊s->d:ε
node[2] or[2]
node[1] or[1]
node[0] s d or[0] a c
5.temp_char=『c』 產生新符號e,產生新邊d->e:c
node[2] or[2]
node[1] or[1]
node[0] s e or[0] a c
首先將當前深度的node棧的棧頂pop到node0中,然後如果or棧不為空,則產生or棧中的每乙個node->node0的邊,權為ε。然後我們再取當前深度node棧的棧頂到node1中。
然後超前檢測下乙個符號是否是『×』,如果是,就產生一條node0到node1的邊,權為ε,然後令node0等於node1(保證*運算為雙目運算子),並且跳過下乙個字元的處理。
然後開始當前括號深度和上一層的連線,首先從上一層的node棧中pop棧頂到node2中,產生一條從node2到node1的邊,權為ε。
然後將上一層的node棧pop到僅剩乙個起始符號,最後將node0壓入上一層的node棧中。
示例:正規表示式:ab((bc)×|d)
0.開始符號 s 壓入node[0]
node[2]
node[1]
node[0] s
1.temp_char=『a』 產生新符號a,產生新邊s->a:a
node[2]
node[1]
node[0] s a
2.temp_char=『b』 產生新符號b,產生新邊a->b:b
node[2] or[2]
node[1] or[1]
node[0] s b or[0]
3.temp_char=』(』 產生新符號c
node[2] or[2]
node[1] c or[1]
node[0] s b or[0]
4.temp_char=』(』 產生新符號d
node[2] d or[2]
node[1] c or[1]
node[0] s b or[0]
5.temp_char=『b』 產生新符號e 產生新邊d->e:b
node[2] d e or[2]
node[1] c or[1]
node[0] s b or[0]
6.temp_char=『c』 產生新符號f 產生新邊e->f:c
node[2] d f or[2]
node[1] c or[1]
node[0] s b or[0]
7.temp_char=』)』 產生新邊f->d:ε,新邊c->d:ε
node[2] or[2]
node[1] c f or[1]
node[0] s b or[0]
8.temp_char=』|』 產生新符號g,新邊c->g:ε
node[2] or[2]
node[1] c g or[1] f
node[0] s b or[0]
9.temp_char=『d』 產生新符號h 產生新邊g->h:d
node[2] or[2]
node[1] c h or[1] f
node[0] s b or[0]
10.temp_char=』)』 產生新邊f->h:ε,新邊b->c:ε
node[2] or[2]
node[1] or[1]
node[0] s h or[0]
#define max_depth 10 //括號最大深度
#define max_node_count 1000 //最大節點數量
node nodes[max_node_count]; //node陣列
#define max_line_count 5000 //最多邊數
line lines[max_line_count]; //邊陣列
node結構體:
struct node;
stack node_stack[max_depth];//node棧
stack or_node_stack[max_depth];//or棧
int brackets=0;//當前括號深度
void creat_new_node(int index);//在index深度的node棧新建乙個符號
//新建乙個從index1指向index2,權為value的邊
void creat_new_line(int index1,int index2,char value);
源**已上傳到碼雲
傳送門:
我自己測試了大約十幾個測試資料,基本都可以通過產生冗餘來完成nfa的構造,但是無法完成不帶括號的閉包運算,算是乙個小小的遺憾吧。
正規表示式轉NFA
最近一直在忙著寫大作業,考試複習,複習演算法的時候寫了一些隨筆,現在忙起來都落下了部落格,這裡有乙個vc 寫的大作業,主要是正規表示式轉nfa並顯示。內容如下。介紹一下nfa在表示的結構設計,由於nfa本身是一種有向圖,所以這裡的儲存結構設計和鄰接表相似,圖中的每個節點後面是一些與其連線的節點的值,...
正規表示式轉NFA
正規表示式有三種基本的運算 連線 concatenation 例如 abc,由a,b,c組成 聯合 union 例如 a b c,表示a或者b或者c kleene閉包 kleene 例如 ab 表示ab串不出現,或者出現1次或一次以上 其它的運算如 等都可以用以上三種基本運算或者運算的組合來表示。2...
編譯原理 正規表示式轉NFA
從txt檔案中讀入正規表示式 include include include include include define max token 100 using namespace std 詞struct token int readtxt string filename,vector token...