編譯原理(三)

一、正則表達式

        正則表達式是一種用來描述正則語言的更緊湊的表示方法。正則表達式可以由較小的正則表達式按照特定的規則遞歸地構建。每個正則表達式r定義(表示)一個語言,記爲L(r)。這個語言也是根據r的子表達式所表示的語言遞歸定義的。

例如:語言L={a}{a,b}* ({ε}∪({.,_}{a,b}{a,b}* ))的正則表達式如下:

                r=a(a|b)*(ε|(.|_)(a|b)(a|b)*)

1.正則表達式的定義

    ε是一個RE,L(ε)={ε};

    a∈∑,則a是一個RE,L(a)={a};

    假設r和s都是RE,表示的語言分別是L(r)和L(s),則

        r|s是一個RE,L(r|s)=L(r)∪L(s)

        rs是一個RE,L(rs)=L(r)L(s)

        r*是一個RE,L(r*)=(L(r))*

        (r)是一個RE,L((r))

    運算符的優先級 :*、連接、| 。

例如:令∑={a,b},則

       L(a|b)=L(a)\cupL(b)={a}\cup{b}={a,b}

       L((a|b)(a|b))=L(a|b)L(a|b)={a,b}{a,b}={aa,ab,ba,bb}

       L(a*)=(L(a))*={a}*={ε,a,aa,aaa,···}

      L((a|b)*)=(L(a|b))*={a,b}*={ε,a,b,aa,ab,ba,bb,aaa,···}

      L(a|a*b)={a,b,ab,aab,aaab,···}

2.正則語言

    可以用RE定義的語言叫做正則語言或者正則集合。

3.正則文法與正則表達式

    對任何正則文法G,存在定義同一語言的正則表達式r,對任何正則表達式r,存在定義同一語言的正則文法G。

4.正則定義  

例1:C語言中標識符的正則定義

        digit→ 0|1|2|···|9

        letter_ → A|B|···|Z|a|b|···|z|_

        id→ letter_(letter_|digit*)

例2:(整數或浮點數)無符號數的正則定義

        digit→ 0|1|2|···|9

        digits→ digit digit*

        optionalFraction → .digits| ε 

        optionalExponent → (E(+|-|ε )digits)|ε 

        number → digits  optionalExponent optionalFraction 

     2             2.15          2.15E+3          2.15E-3           2.15E3           2E-3

二、確定的有限自動機

唯一的初始狀態,轉換函數是單值部分映射。

三、非確定的有限自動機

轉換函數對某個輸入,可以達到多個狀態。

四、從正則表達式到有限自動機

正規文法<=>有限自動機<=>正規式 只要他們接受的語言相同,則他們等價。

1.RE轉換NFA

2.NFA轉換DFA(確定化)

    子集構造法:NFA轉換DFA的方法(狀態轉換矩陣)

三個重要運算:

    狀態集的ε-閉包:狀態集I中的任何狀態s及經任意條ε弧而能到達的所有狀態的集合,定義爲狀態集I的ε-閉包,表示爲ε-closure(I)。

    狀態集的a弧轉換:狀態集I中的任何狀態s經過一條a弧而能到達的所有狀態的集合,定義爲狀態集I的a弧轉換,表示爲move(I, a)。對於任意 NFA M=(K,Σ,f,S,F),

I包含於K,a∈Σ,不妨設I={s1,s2,…sj },則move(I,a)=f(s1,a)∪f(s2,a) ∪…∪f(sj,a)。

    狀態集的a弧轉換的閉包:Ia=ε-closure(move(I,a))

例題:下圖所示NFA,轉換爲DFA。

解析:

    對於I={0},ε-closure(I)=ε-closure({0})={0,1,2,4,7},

    若I={2,3},ε-closure(I)=ε-closure({2,3})={1,2,3,4,6,7},

    令I ={0,1,2,4,7},則move(I,a)={3,8},move(I,b)={5},

            Ia=ε-closure(move(I,a))=ε-closure({3,8})={1,2,3,4,6,7,8} 

            Ib=ε-closure(move(I,b))=ε-closure({5})={1,2,4,5,6,7}

3.DFA的最小化

     對於任意一個DFA M構造另一個DFA M' ,使L(M)=L(M'),並且M'的狀態個數不多於M的狀態個數。

        多餘狀態:對於一個狀態Si ,若從開始狀態出發,不可能到達該狀態Si,則Si爲多餘(無用)狀態。 S1,S5,S6爲多餘狀態。

        死狀態:對於一個狀態Si,對任意輸入符號a,若轉到它本身後,不可能從它到達終止狀態,則稱爲Si爲死狀態。S2爲死狀態。多餘狀態和死狀態又稱爲無關狀態。

         等價狀態:若Si爲自動機的一個狀態,我們把從Si出發能導出的所有符號串集合記爲L(Si)。設有兩個狀態Si和Sj,若有L(Si)=L(Sj),則稱Si和Sj是等價狀態。S1和S2是等價狀態。

        可區別狀態:自動機中的兩個狀態Si和Sj,如果它們不等價,則稱它們是可區別的。狀態Si和Sj 必須同時是終止狀態或同時是非終止狀態,即終止狀態和非終止狀態是可區別的;狀態Si和Sj對於任意輸入符號a∈∑,必須轉到等價的狀態裏,否則Si和Sj是可區別的。S0、S1、S2和S3是可區別的,S0和S2是可區別的。

DFA的最簡化(最小化)的步驟:對於DFA M=(S,Σ,f,S0,Z) 

    1.首先將DFA的狀態集進行初始化,分成π=(S-Z,Z);      //非終態、終態

    2.用下面的過程對Π構造新的劃分π new: 

        對Π中每個組G,G中的任意兩個狀態Si和Sj在同一組中,當且僅當對於Σ中任意輸入符號 a ,Si和Sj的a轉換是到同一組中,move(Si, a) ∈Gi ,move(Sj, a) ∈Gi。只要Si和Sj的a轉換是到不同的組中,則說明Si和Sj是可區別的,可進行劃分。

      在Π new中用剛完成的對G的劃分代替原來的G。 

    3.重複執行(2),直到Π中每個狀態集不能再劃分 (π new=π)爲止;

    4.合併等價狀態,在每個G中,取任意狀態作爲代表,刪去其它狀態; 

    5.刪去無關狀態,從其它狀態到無關狀態的轉換都成爲無定義。 

例題:將DFA最小化

解析:

    第一步:首次劃分,Π0=({A,B,C,D},{E});

    第二步:在G={A,B,C,D}中,f(A,a)=B,f(B,a)=B,f(C,a)=B,f(D,a)=B,f(A,b)=C,f(B,b)=D,f(C,b)=C,f(D,b)=E(終態),故可以劃分爲{A,B,C}和{D},Π1=({A,B,C},{D},{E});

    第三步:在G={A,B,C}中,f(A,a)=B,f(B,a)=B,f(C,a)=B,f(A,b)=C,f(B,b)=D,f(C,b)=C,故{A,B,C}可劃分爲{A,C}和{B},Π2=({A,C},{B},{D},{E});

    第四步:在G={A,C}中,不可再分,則將A、C作爲一個狀態。

                   刪去C,再將原來導入C的弧導入它的代表狀態A。

補充內容:

   逆波蘭式(Reverse Polish notation,RPN,或逆波蘭記法),也叫後綴表達式(將運算符寫在操作數之後)

(a+b)*c-(a+b)/e  →((a+b)*c)((a+b)/e)-   →((a+b)c*)((a+b)e/)-   →(ab+c*)(ab+e/)-     →ab+c*ab+e/-

算法表示:

    將一個普通的中序表達式轉換爲逆波蘭表達式的一般算法是:

    首先需要分配2個棧,一個作爲臨時存儲運算符的棧S1(含一個結束符號),一個作爲輸入逆波蘭式的棧S2(空棧),S1棧可先放入優先級最低的運算符#,注意,中綴式應以此最低優先級的運算符結束。可指定其他字符,不一定非#不可。從中綴式的左端開始取字符,逐序進行如下步驟:

    (1)若取出的字符是操作數,則分析出完整的運算數,該操作數直接送入S2棧

    (2)若取出的字符是運算符,則將該運算符與S1棧棧頂元素比較,如果該運算符優先級(不包括括號運算符)大於S1棧棧頂運算符優先級,則將該運算符進S1棧,否則,將S1棧的棧頂運算符彈出,送入S2棧中,直至S1棧棧頂運算符低於(不包括等於)該運算符優先級,最後將該運算符送入S1棧。

    (3)若取出的字符是“(”,則直接送入S1棧頂。

    (4)若取出的字符是“)”,則將距離S1棧棧頂最近的“(”之間的運算符,逐個出棧,依次送入S2棧,此時拋棄“(”。

    (5)重複上面的1~4步,直至處理完所有的輸入字符

    (6)若取出的字符是“#”,則將S1棧內所有運算符(不包括“#”),逐個出棧,依次送入S2棧。

完成以上步驟,S2棧便爲逆波蘭式輸出結果。不過S2應做一下逆序處理。便可以按照逆波蘭式的計算方法計算了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章