課程名:形式語言與自動機
作者:Lupinus_Linn
許可證:CC-BY-NC-SA 3.0 創作共用-署名-非商業性-相同方式共享
署名 (英語:Attribution,BY):您(用戶)可以複製、發行、展覽、表演、放映、廣播或通過信息網絡傳播本作品;您必須按照作者或者許可人指定的方式對作品進行署名。
非商業性使用 (英語:Noncommercial,NC):您可以自由複製、散佈、展示及演出本作品;您不得爲商業 目的而使用本作品。
相同方式共享 (英語:Sharealike,SA):您可以自由複製、散佈、展示及演出本作品;若您改變、轉變或更改本作品,僅在遵守與本作品相同的許可條款下,您才能散佈由本作品產生的派生作品。(參見copyleft 。)
引用:
本文中部分文字與圖片引用自北京郵電大學計算機學院王柏教授的《形式語言與自動機》課程課件。
緒論中的證明方法部分引自清華大學王生原老師課件。
部分題目插圖引用自北京郵電大學出版社《形式語言與自動機 第二版》教材。
在此一併表示感謝,並不做商業用途。
本筆記所有內容的傳送門
Part.1緒論, Part.2 語言與文法
Part 3.有限自動機
Part.4 正則語言,2DFA,Mealy&Moore機
Part.5 上下文無關語言與下推自動機(PDA)
Part.6 圖靈機
Part 5. 上下文無關文法和下推自動機
5.1 上下文無關文法
回顧Chomsky文法體系,上下文無關文法是2型文法。
產生式形如A → α , A ∈ N , α ∈ ( N ∪ T ) ∗ A\rarr \alpha ,A\in N,\alpha\in(N\cup T)^* A → α , A ∈ N , α ∈ ( N ∪ T ) ∗ 的語言是上下文無關語言。(左側是單個非終結符)
2型文法對應的識別器是下推自動機PDA,其比普通的自動機多了一個棧,稱爲下推棧。
5.2 推導與歸約
最右推導 :若推導過程的每一步 總是替換出現在最右邊的非終結符 , 則這樣的推導稱爲最右推導(rm,rightmost).
最左推導 :反之。
推導樹 :文法的起始符爲根,樹的枝結點標記是非終結符, 葉結點標記爲終結符或ε。
若枝結點A A A 有直接子孫x 1 , x 2 , … , x k x_1, x_2,…, x_k x 1 , x 2 , … , x k ,則文法中有生成式A → x 1 x 2 … x k A→x_1x_2…x_k A → x 1 x 2 … x k .
例子:
推導樹邊緣 :葉子從左向右組成的字符串稱爲推導樹的邊緣。
歸約 :又稱遞歸推理。自下而上,從句型還原出使用的文法。
歸約過程:將產生式的右部(body)替換爲產生式 的左部( head).
推導和歸約分別是從上到下/從下到上建樹。
以下幾個描述是一回事
字符串ω ∈ T ∗ \omega \in T^* ω ∈ T ∗ 可以歸約到非終結符A A A .
存在一棵根節點爲A的推導樹,其邊緣爲ω \omega ω .
非終結符A A A 可以推導出串ω \omega ω .
非終結符A A A 可以最左推導出串ω \omega ω .
非終結符A A A 可以最右推導出串ω \omega ω .
5.3 二義性
二義性是針對文法而言的。
上下文無關文法G G G 是二義的↔ \leftrightarrow ↔ 存在兩棵不同的推導樹,但是它們的邊緣是同一個串ω ∈ L ( G ) \omega\in L(G) ω ∈ L ( G )
如果文法是二義的,那麼它產生的某個句子可以從不同的最左/右推導推出。
例子:
對於同一語言,可能存在有二義性和沒有二義性的不同文法。但是沒有一般方法可以通過變換消除二義性。
5.4 CFG的Chomsky範式(CNF)
要求:每個非終結符要麼推出兩個非終結符,要麼推出一個終結符。即:生成式形式爲A → B C , A → a , A , B , C ∈ N , a ∈ T A→BC, A→a, A, B, C∈N , a∈T A → B C , A → a , A , B , C ∈ N , a ∈ T
本質特徵:推導樹是二叉樹。
每個CFG都可以化成CNF.
5.5 CFG的Grebach範式(GNF)
要求:每個產生式右端都以終結符開始,且只有該一個終結符(但推導樹未必是二叉樹)。即:生成式形式爲A → a β , a ∈ T , β ∈ N ∗ A→aβ, a∈T , β∈N^* A → a β , a ∈ T , β ∈ N ∗
本質特徵:消除左遞歸。
5.6 消去CFG的無用符號
有用符號 :包含生成符號和可達符號。即:X ∈ N ∪ T X\in N \cup T X ∈ N ∪ T 是有用的⇔ \Leftrightarrow ⇔ S ⇒ ∗ α X β ⇒ ∗ ω , ω ∈ T ∗ S\Rightarrow^* \alpha X \beta\Rightarrow^*\omega ,\omega\in T^* S ⇒ ∗ α X β ⇒ ∗ ω , ω ∈ T ∗
生成符號 :可以推出非終結符串的符號。即:∃ ω ∈ T ∗ \exists \omega \in T^* ∃ ω ∈ T ∗ ,X X X 推出ω \omega ω .
可達符號 :可以被起始符號到達的符號,即:S S S 推出α X β \alpha X \beta α X β .
無用符號 :非生成符號和不可達符號。
消去無用符號 :將無用符號從N N N 中移除,並移除其在P P P 中的所有產生式。
必須先刪除非生成符號,再刪除不可達符號。 如果反着來會出現:某個符號原先是可達的,但是和其一起的某個符號因爲不是生成符號被刪除了,就不可達了。導致該符號沒有被正常刪除。
5.6.1 找出生成符號集
精髓 :從終結符倒着推,能推出終結符串的就是生成符號。
方法 :
基礎 任何終結符 a ∈ T a\in T a ∈ T 都是生成符號。
歸納 如果有產生式 A → α A\to \alpha A → α ,其中 α ∈ ( N ∪ T ) ∗ \alpha \in (N\cup T)^* α ∈ ( N ∪ T ) ∗ 的每一個符號都是生成符號,則 A 也是生成符號。
5.6.2 找出可達符號集
精髓 :起始符號是可達符號,可達符號產生式右側都是可達符號。
5.7 消去CFG的ϵ \epsilon ϵ 產生式(不是消符號)
意義 :消去ϵ \epsilon ϵ 產生式外,除了文法不能產生空串外,沒有影響。
可致空符號 :可以經過一步或多步推出空串的符號。即:對於C F G G = ( N , T , P , S ) CFG\ G = (N, T, P, S ) C F G G = ( N , T , P , S ) ,稱符號 A ∈ N A \in N A ∈ N 是可致空的,當且僅當A A A 可以經過一步或多步推出ϵ \epsilon ϵ .
5.7.1 找出可致空符號集
精髓 :能直接推出ϵ \epsilon ϵ 的符號肯定是可致空符號,能推出全 可致空符號串的也是可致空符號。
方法 :
基礎 對於所有產生式 A → ϵ A \to \epsilon A → ϵ ,A A A 是一個可致空符號
歸納 如果有產生式 B → C 1 C 2 … C k B\to C_1C_2…C_k B → C 1 C 2 … C k ,其中每一個 C i ∈ N C_i
\in N C i ∈ N 是可致空符號,則 B B B 是一個可致空符號。
5.7.2 消去空產生式
所有直接推出ϵ \epsilon ϵ 的產生式被刪除。
推出可致空符號串的產生式,由於需要表現出原來推出空串的可能性,需要將其所有爲空的可能性枚舉出來,但是不能是空串。
比如A->C1C2C3,其中C1C2C3都是可空符號
就有以下的可能
A->C1C2C3
A->C1C2
A->C1C3
A->C2C3
A->C1
A->C2
A->C3
當C1,C2,C3不可空之後,就要將這些產生式全部加到A裏
最後還要處理起始符號,如果能產生空串,就讓新起始符號直接產生空串。
步驟 :
找出能推導出ϵ \epsilon ϵ 的所有非終結符A A A 的集合N ′ N' N ′ .
按如下兩步組成新的P 1 P_1 P 1
① 如果生成式A → β 0 C 0 β 1 C 1 … C n β n ∈ P , n ≥ 0 A\to \beta_0C_0\beta_1C_1\dots C_n\beta_n\in P,n\ge 0 A → β 0 C 0 β 1 C 1 … C n β n ∈ P , n ≥ 0 C k ∈ N ′ ( 1 ≤ k ≤ n ) , β j ∉ N ′ C_k \in N'(1\le k\le n),\beta_j \notin N' C k ∈ N ′ ( 1 ≤ k ≤ n ) , β j ∈ / N ′ (即可空符號裏插了一些不可空符號),則P 1 P_1 P 1 應加入A → β 0 Y 0 β 1 Y 1 … Y n β n A\to \beta_0Y_0\beta_1Y_1\dots Y_n\beta_n A → β 0 Y 0 β 1 Y 1 … Y n β n ,其中Y k Y_k Y k 取C k C_k C k 或者ϵ \epsilon ϵ 。(即保持不可空符號),枚舉所有的情況,但不包括A → ϵ A\to \epsilon A → ϵ .
如果S ∈ N ′ S\in N' S ∈ N ′ (即S可空),則P 1 P_1 P 1 中增加S 1 → ϵ ∣ S S_1\to \epsilon|S S 1 → ϵ ∣ S ,S 1 S_1 S 1 是新的起始符,N 1 = N ∪ { S 1 } N_1=N\cup \{S_1\} N 1 = N ∪ { S 1 } 。(即把起始符推出空串單獨寫出來)。
如果S ∉ N ′ S\notin N' S ∈ / N ′ (即S不可空),則繼承起始符即可。
得到的無ϵ \epsilon ϵ 文法G 1 = ( N 1 , T , P 1 , S 1 ) G_1=(N_1,T,P_1,S_1) G 1 = ( N 1 , T , P 1 , S 1 ) .
例子 :
5.8 消去CFG的單產生式(不是消符號)
單產生式 :一個非終結符推出一個非終結符的產生式,說了一句廢話。
單元偶對 :一個非終結符如果能全部使用單產生式推出另一個非終結符,則這兩個非終結符是一個單元偶對。
比如A->B B->C
則實際上有A->B->C
則(A,B),(A,C),(B,C)都是單元偶對
5.8.1 找出所有的單元偶對
精髓 :從S開始BFS,根據單產生式的關係畫出樹即可。
方法 :這裏從某一個不特定的非終結符A爲起點。
設A的單元偶對的非終結符的集合是N A N_A N A ,設N = { A } N=\{A\} N = { A } 開始迭代。
遍歷N N N 內的非終結符(迭代變量B B B ),遍歷B B B 的產生式,如果B → C B\to C B → C ,則N N N 增加非終結符C C C .
重複2.,直到N N N 內的非終結符不再增加。
5.8.2 消去單產生式
精髓 :兒子的產生式全部交給爸爸。
如果A->B B->α
則去掉A->B,增加A->α
步驟 :如果B → α ∈ P B\to \alpha \in P B → α ∈ P ,則B B B 的所有爸爸,即滿足B ∈ N A B\in N_A B ∈ N A 的所有A A A ,把A → α A\to \alpha A → α 加入到P 1 P_1 P 1 .
最終得到G 1 = ( N 1 , T 1 , P 1 , S ) G_1=(N_1,T_1,P_1,S) G 1 = ( N 1 , T 1 , P 1 , S ) .
注意 :有可能有成環的情況,此時要一直推到只包含自己到自己這一條單產生式,不能再含有其他的單產生式。
A->B|α B->C|β C->A|γ
A->B|α
A->C|β|α
A->A|γ|β|α
A->A是一句正確的廢話,刪掉
A->γ|β|α
B同理,推到A的時候可以用A最後的結果
B->C|β
B->A|γ|β
B->γ|β|α|β
刪掉重複的
B->γ|β|α
5.9 化簡順序:消空→消單→消無用
如果消空前的符號比較多可以先消一次無用。消無用什麼時候都可以做,但是消無用內部需要先消非生成符號 ,再消不可達符號 。
5.10 消除遞歸
遞歸文法 :如果某個非終結符能推出含有自己 的句型(倒不是有自己到自己的產生式),則稱這個文法是遞歸文法。即:A推出α A β , A ∈ N \alpha A\beta,A\in N α A β , A ∈ N ,則該文法是遞歸文法。
左遞歸文法 :能經過若干步後推出含有自己的句型,並且自己在左邊,即:A推出A β A\beta A β ,則文法是左遞歸文法。
右遞歸文法 :能經過若干步後推出含有自己的句型,並且自己在右邊。
循環文法 :能經過若干步後推出自己。即:A推出A。
生成式 :CFG中,所有A → α A\to \alpha A → α 的生成式稱爲A A A 生成式。
遞歸在推導樹上的表現就是子樹
下圖中推導n次會得到a n S b n a^nSb^n a n S b n
5.10.1 消除直接左遞歸
假設A A A 產生式是A → A α ∣ β A\to A\alpha | \beta A → A α ∣ β
按照下列推導樹得到β α 2 \beta \alpha^2 β α 2
可以看出,每次使用A → A α A\to A\alpha A → A α 產生式會在右邊生成一個α \alpha α ,並且越早產生的α \alpha α 在最後的句子中越靠右。
最後一定有一個β \beta β 在最左邊。
所以不妨先至少推出一個β \beta β ,然後用右線性文法產生α ∗ \alpha^* α ∗ ,這樣就消除了左遞歸。
改寫爲
A → β ∣ β A ′ A ′ → α ∣ α A ′
A\to \beta|\beta A'\\
A'\to \alpha|\alpha A'
A → β ∣ β A ′ A ′ → α ∣ α A ′
當產生式有多個直接左遞歸時,每次可以選擇的產生式是不定的,所以在文法中將所有的可能列出來。
A → A α 1 ∣ A α 2 ∣ … ∣ A α m ∣ β 1 ∣ β 2 ∣ … ∣ β n
A\to A\alpha_1|A\alpha_2|\dots |A\alpha_m|\beta_1|\beta_2|\dots|\beta_n
A → A α 1 ∣ A α 2 ∣ … ∣ A α m ∣ β 1 ∣ β 2 ∣ … ∣ β n
可以改寫爲
A → β 1 ∣ β 2 ∣ … ∣ β n ∣ β 1 A ′ ∣ β 2 A ′ ∣ … ∣ β n A ′ A ′ → α 1 ∣ α 2 ∣ … ∣ α m ∣ α 1 A ′ ∣ α 2 A ′ ∣ … ∣ α m A ′
A\to \beta_1|\beta_2|\dots|\beta_n|\beta_1A'|\beta_2A'|\dots|\beta_nA'\\
A'\to \alpha_1|\alpha_2|\dots|\alpha_m|\alpha_1A'|\alpha_2A'|\dots|\alpha_mA'
A → β 1 ∣ β 2 ∣ … ∣ β n ∣ β 1 A ′ ∣ β 2 A ′ ∣ … ∣ β n A ′ A ′ → α 1 ∣ α 2 ∣ … ∣ α m ∣ α 1 A ′ ∣ α 2 A ′ ∣ … ∣ α m A ′
即,產生且僅產生{ β n } \{\beta_n\} { β n } 中的一個,在產生{ α m } ∗ \{\alpha_m\}^* { α m } ∗ 時,每一次的選擇是獨立的,可以選擇不同的α \alpha α .
最後得到的正則式是
( β 1 + β 2 + ⋯ + β n ) ( α 1 + ⋯ + α m ) ∗
(\beta_1+\beta_2+\dots+\beta_n)(\alpha_1+\dots+\alpha_m)^*
( β 1 + β 2 + ⋯ + β n ) ( α 1 + ⋯ + α m ) ∗
例子:
這裏的*是一個字符。
5.10.2 消除全部左遞歸
5.10.2.1 理解
還有一些左遞歸不是直接的,比如
A 1 → A 2 α 1 ∣ β 1 A 2 → A 1 α 2 ∣ β 2
A_1\to A_2\alpha_1|\beta_1\\
A_2\to A_1\alpha_2|\beta_2
A 1 → A 2 α 1 ∣ β 1 A 2 → A 1 α 2 ∣ β 2
顯然A 1 ⇒ A 2 α 1 ⇒ A 1 α 2 α 1 A_1\Rightarrow A_2\alpha_1\Rightarrow A_1\alpha_2\alpha_1 A 1 ⇒ A 2 α 1 ⇒ A 1 α 2 α 1 ,仍然會產生左遞歸。
甚至這個套娃鏈可以更長
A 1 → A 2 α 1 ∣ β 1 A 2 → A 3 α 2 ∣ β 2 A 3 → A 4 α 3 ∣ β 3 A 4 → A 1 α 4 ∣ β 4
A_1\to A_2\alpha_1|\beta_1\\
A_2\to A_3\alpha_2|\beta_2\\
A_3\to A_4\alpha_3|\beta_3\\
A_4\to A_1\alpha_4|\beta_4
A 1 → A 2 α 1 ∣ β 1 A 2 → A 3 α 2 ∣ β 2 A 3 → A 4 α 3 ∣ β 3 A 4 → A 1 α 4 ∣ β 4
回到只有兩個非終結符A 1 , A 2 A_1,A_2 A 1 , A 2 的例子,生成式的形式有點像二元一次方程(注意只是類比,有不同點)
x 1 = x 2 α 1 + β 1 x 2 = x 1 α 2 + β 2
x_1=x_2\alpha_1+\beta_1\\
x_2=x_1\alpha_2+\beta_2
x 1 = x 2 α 1 + β 1 x 2 = x 1 α 2 + β 2
解這種方程的方法是:
① 代入x 2 x_2 x 2
x 1 = ( x 1 α 2 + β 2 ) α 1 + β 1 x 1 = x 1 α 2 α 1 + β 2 α 1 + β 1
x_1=(x_1\alpha_2+\beta_2)\alpha_1+\beta_1\\
x_1=x_1\alpha_2\alpha_1+\beta_2\alpha_1+\beta_1
x 1 = ( x 1 α 2 + β 2 ) α 1 + β 1 x 1 = x 1 α 2 α 1 + β 2 α 1 + β 1
② 解一元一次方程:x 1 = x 1 α 2 α 1 + β 2 α 1 + β 1 x_1=x_1\alpha_2\alpha_1+\beta_2\alpha_1+\beta_1 x 1 = x 1 α 2 α 1 + β 2 α 1 + β 1
③ 將解出的x ˙ 1 \dot x_1 x ˙ 1 帶回到其他方程,得x ˙ 2 = x ˙ 1 α 2 + β 2 \dot x_2=\dot x_1\alpha_2+\beta_2 x ˙ 2 = x ˙ 1 α 2 + β 2
結合線性文法的特點,可以有如下解法
對於
A 1 → A 2 α 1 ∣ β 1 A 2 → A 1 α 2 ∣ β 2
A_1\to A_2\alpha_1|\beta_1\\
A_2\to A_1\alpha_2|\beta_2
A 1 → A 2 α 1 ∣ β 1 A 2 → A 1 α 2 ∣ β 2
① 將A 1 A_1 A 1 中A 2 A_2 A 2 在最左邊的產生式,做一次推導,將其作爲一個新產生式(有個定理說的是推導可以作爲產生式,我沒寫)
∵ A 1 ⇒ A 1 α 2 α 1 ∣ β 1 ∴ A 1 → A 1 α 2 α 1 ∣ β 1
\because A_1\Rightarrow A_1\alpha_2\alpha_1|\beta_1\\
\therefore A_1\to A_1\alpha_2\alpha_1|\beta_1
∵ A 1 ⇒ A 1 α 2 α 1 ∣ β 1 ∴ A 1 → A 1 α 2 α 1 ∣ β 1
② 根據消去直接左遞歸的方法,消去A 1 A_1 A 1 的左遞歸
A 1 → β 1 ∣ β 1 A 1 ′ A 1 ′ → α 2 α 1 ∣ α 2 α 1 A 1 ′
A_1\to \beta_1|\beta_1A'_1\\
A'_1\to \alpha_2\alpha_1|\alpha_2\alpha_1A'_1
A 1 → β 1 ∣ β 1 A 1 ′ A 1 ′ → α 2 α 1 ∣ α 2 α 1 A 1 ′
③ 將解出的非左遞歸A 1 A_1 A 1 帶回A 2 A_2 A 2
A 2 → β 1 ∣ β 1 A 1 ′ ∣ β 2
A_2\to \beta_1|\beta_1A'_1|\beta_2
A 2 → β 1 ∣ β 1 A 1 ′ ∣ β 2
此時A 2 A_2 A 2 也沒有了左遞歸。
④ 得出結果
A 1 → β 1 ∣ β 1 A 1 ′ A 2 → β 1 ∣ β 1 A 1 ′ ∣ β 2 A 1 ′ → α 2 α 1 ∣ α 2 α 1 A 1 ′
A_1\to \beta_1|\beta_1A'_1\\
A_2\to \beta_1|\beta_1A'_1|\beta_2\\
A'_1\to \alpha_2\alpha_1|\alpha_2\alpha_1A'_1
A 1 → β 1 ∣ β 1 A 1 ′ A 2 → β 1 ∣ β 1 A 1 ′ ∣ β 2 A 1 ′ → α 2 α 1 ∣ α 2 α 1 A 1 ′
推廣到一般時,需要注意
間接左遞歸是一個圈,即對於一組含有間接左遞歸的產生式,以任何一個非終結符作爲左部都可以推出直接左遞歸。所以需要對非終結符排序。
排序不一定是按左遞歸鏈條的,比如明明鏈條是A 1 ⇒ A 2 ⋯ ⇒ A 3 … A_1\Rightarrow A_2\dots\Rightarrow A_3\dots A 1 ⇒ A 2 ⋯ ⇒ A 3 … ,執行算法時也可以排成A 2 , A 1 , A 3 A_2,A_1,A_3 A 2 , A 1 , A 3 的順序。
5.10.2.2 算法
將全部非終結符排序,即將N N N 映射到A 1 , A 2 , … , A m A_1,A_2,\dots,A_m A 1 , A 2 , … , A m .
消除{ A m } \{A_m\} { A m } 中所有的直接左遞歸。
從A 1 A_1 A 1 開始,到A m A_m A m 結束
假設現在執行的是A i A_i A i ,找出類似左遞歸形式的A i → A j … A_i\to A_j\dots A i → A j … ,如果j < i j\lt i j < i ,則將A j A_j A j 代換。
迭代保證了A j → A k … A_j\to A_k\dots A j → A k … 中一定滿足k > j k\gt j k > j ,因爲比j j j 小的已經在之前迭代的時候換過了,而直接左遞歸已經在第二步被消掉了。
實際在操作的時候可以把第2、3步結合做,即一遍消直接左遞歸,一遍代入比自己小的。
例子:
A 1 → A 2 A 3 ∣ a A 2 → A 3 A 1 ∣ A 1 b A 3 → A 1 A 2 ∣ A 3 A 3 ∣ a
A_1\to A_2A_3|a\\
A_2\to A_3A_1|A_1b\\
A_3\to A_1A_2|A_3A_3|a
A 1 → A 2 A 3 ∣ a A 2 → A 3 A 1 ∣ A 1 b A 3 → A 1 A 2 ∣ A 3 A 3 ∣ a
排序:{ A 1 , A 2 , A 3 } \{A_1,A_2,A_3\} { A 1 , A 2 , A 3 }
採用一遍消直接左遞歸一遍代入比自己小的的策略。
A 1 A_1 A 1 :
沒有直接左遞歸,沒有比自己小的A 1 → A j … A_1\to A_j\dots A 1 → A j …
A 2 A_2 A 2 :
沒有直接左遞歸,有比自己小的A 2 → A 1 b A_2\to A_1 b A 2 → A 1 b
所以代入一次A 2 → ( A 2 A 3 + a ) b ∣ A 3 A 1 → A 2 A 3 b ∣ A 3 A 1 ∣ a b A_2\to (A_2A_3+a)b|A_3A_1\to A_2A_3b|A_3A_1|ab A 2 → ( A 2 A 3 + a ) b ∣ A 3 A 1 → A 2 A 3 b ∣ A 3 A 1 ∣ a b
是直接左遞歸,消掉,其中β 1 = A 3 A 1 , β 2 = a b , α = A 3 b \beta_1 = A_3A_1,\beta_2 = ab,\alpha = A_3b β 1 = A 3 A 1 , β 2 = a b , α = A 3 b
A 2 → β 1 ∣ β 2 ∣ β 1 A 2 ′ ∣ β 2 A 2 ′ a k a . A 2 → A 3 A 1 ∣ a b ∣ A 3 A 1 A 2 ′ ∣ a b A 2 ′ A 2 ′ → α ∣ α A 2 ′ a k a . A 2 ′ → A 3 b ∣ A 3 b A 2 ′
A_2 \to \beta_1| \beta_2 | \beta_1 A'_2 | \beta_2 A'_2\\
aka.A_2\to A_3A_1|ab|A_3A_1A'_2|abA'_2\\
A'_2\to \alpha | \alpha A'_2\\
aka.A'_2 \to A_3b | A_3bA'_2
A 2 → β 1 ∣ β 2 ∣ β 1 A 2 ′ ∣ β 2 A 2 ′ a k a . A 2 → A 3 A 1 ∣ a b ∣ A 3 A 1 A 2 ′ ∣ a b A 2 ′ A 2 ′ → α ∣ α A 2 ′ a k a . A 2 ′ → A 3 b ∣ A 3 b A 2 ′
A 3 A_3 A 3 太多了我懶得寫了
5.11 轉Chomsky範式
這裏的前提是已經是無空產生式、無單產生式、無循環、無無用符號的文法了。(前兩者對CNF範式很重要)
精髓 :因爲CNF範式只允許A → B C , A → a A\to BC,A\to a A → B C , A → a ,所以產生式右部長度大於2時,就把最左符號單拎出來,其他符號用臨時符號代替。而且因爲不允許A → a B A\to aB A → a B ,所以非終結符出現在非葉節點需要添加非終結符,改寫成A → C a B , C a → a A\to C_aB,C_a\to a A → C a B , C a → a .
方法 :對於產生式A → D 1 D 2 … D n , n ≥ 2 A\to D_1D_2\dots D_n,n\ge 2 A → D 1 D 2 … D n , n ≥ 2 ,假設引入的非終結符形如B i B_i B i
如果D i ∈ T D_i\in T D i ∈ T ,則引入B i → D i B_i\to D_i B i → D i
如果D i ∈ N D_i \in N D i ∈ N ,則令B i → D i B_i\to D_i B i → D i .(形式化描述用,實際不用)
經過上面兩種情況,原來的生成式變成了A → B 1 B 2 … B n , n ≥ 2 A\to B_1B_2\dots B_n,n\ge 2 A → B 1 B 2 … B n , n ≥ 2
如果n > 2 n\gt 2 n > 2 ,則引入C i C_i C i ,兩兩分組。
A → B 1 C 1 C 1 → B 2 C 2 … C n − 1 → B n − 1 B n
A\to B_1C_1\\
C_1\to B_2 C_2\\
\dots\\
C_{n-1}\to B_{n-1}B_n
A → B 1 C 1 C 1 → B 2 C 2 … C n − 1 → B n − 1 B n
例子 :G = ( { A , B , S } , { a , b } , P , S ) G=(\{A,B,S\},\{a,b\},P,S) G = ( { A , B , S } , { a , b } , P , S ) ,求等效C N F G 1 CNF\ G_1 C N F G 1 .
P : S → a A B ∣ B A A → B B B ∣ a B → A S ∣ b
P:
S\to aAB|BA\\
A\to BBB|a\\
B\to AS|b
P : S → a A B ∣ B A A → B B B ∣ a B → A S ∣ b
解:化成無空、無單、無循環、無無用符號的文法。這裏已經是了。
已經符合CNF範式的,不變
S → B A , A → a , B → A S ∣ b
S\to BA,A\to a,B\to AS|b
S → B A , A → a , B → A S ∣ b
保留。
對於S → a A B S\to aAB S → a A B ,變爲
S → C a A B S → C a C 1 , C 1 → A B
S\to C_aAB\\
S\to C_aC_1,C_1\to AB
S → C a A B S → C a C 1 , C 1 → A B
對於A → B B B A\to BBB A → B B B ,變爲A → B C 2 , C 2 → B B A\to BC_2,C_2\to BB A → B C 2 , C 2 → B B .
5.12 轉Greibach範式
需要先轉成CNF.
複習:GNF範式的特點是生成式右部最左一定是終結符,但是不一定是二叉樹。
精髓 :消直接左遞歸的步驟可以將生成式右部最左引入終結符,符合GNF的要求。所以消掉全部的左遞歸,其他非左遞歸成分的不斷代入,直到出現最左是終結符的爲止。
例子 :已有CNF如下(其他略去不表)
P : A → B C B → C A ∣ b C → A B ∣ a
P:A\to BC\\
B\to CA|b\\
C\to AB|a
P : A → B C B → C A ∣ b C → A B ∣ a
將非終結符排序:A,B,C
執行消全部左遞歸算法:
A , B A,B A , B 的產生式不需要調整。
C → A B C\to AB C → A B 的最左A < C A\lt C A < C ,需要代入C → B C B C\to BCB C → B C B ,仍然不行,再代入C → C A C B ∣ b C B C\to CACB|bCB C → C A C B ∣ b C B 。不要忘了C → a C\to a C → a ,代換後得到的C C C 產生式是
C → C A C B ∣ b C B ∣ a
C\to CACB|bCB|a
C → C A C B ∣ b C B ∣ a
對於C → C A C B C\to CACB C → C A C B ,消左遞歸
α = A C B , β 1 = b C B , β 2 = a C → β 1 ∣ β 2 ∣ β 1 C ′ ∣ β 2 C ′ → b C B ∣ a ∣ b C B C ′ ∣ a C ′ C ′ → α ∣ α C ′ → A C B ∣ A C B C ′
\alpha = ACB,\beta_1 = bCB,\beta_2 = a\\
C\to \beta_1|\beta_2|\beta_1C'|\beta_2C'\to bCB|a|bCBC'|aC'\\
C'\to \alpha|\alpha C'\to ACB| ACBC'
α = A C B , β 1 = b C B , β 2 = a C → β 1 ∣ β 2 ∣ β 1 C ′ ∣ β 2 C ′ → b C B ∣ a ∣ b C B C ′ ∣ a C ′ C ′ → α ∣ α C ′ → A C B ∣ A C B C ′
倒序遍歷所有非終結符,將不符合GNF要求的(右部最左爲非終結符)不斷代換,直到符合要求。
然後倒序新引入的用於消左遞歸的符號X ′ X' X ′ ,同樣操作。
C C C 的產生式都符合要求。
B B B 的產生式B → C A B\to CA B → C A 不符合要求,代入
B → C A → b C B A ∣ a A ∣ b C B C ′ A ∣ a C ′ A
B\to CA\to bCBA|aA|bCBC'A|aC'A
B → C A → b C B A ∣ a A ∣ b C B C ′ A ∣ a C ′ A
A A A 的產生式A → B C A\to BC A → B C 不符合要求,代入
A → B C → b C B A C ∣ a A C ∣ b C B C ′ A C ∣ a C ′ A C
A\to BC\to bCBAC|aAC|bCBC'AC|aC'AC
A → B C → b C B A C ∣ a A C ∣ b C B C ′ A C ∣ a C ′ A C
原有非終結符處理完畢,處理新引入的符號。
C ′ C' C ′ 的產生式C ′ → A C B ∣ A C B C ′ C'\to ACB| ACBC' C ′ → A C B ∣ A C B C ′ 不符合要求,代入A A A
C ′ → b C B A C C B ∣ a A C C B ∣ b C B C ′ A C C B ∣ a C ′ A C C B ∣ b C B A C C B C ′ ∣ a A C C B C ′ ∣ b C B C ′ A C C B C ′ ∣ a C ′ A C C B C ′
C' \to bCBACCB|aACCB|bCBC'ACCB|aC'ACCB| bCBACCBC'|aACCBC'|bCBC'ACCBC'|aC'ACCBC'
C ′ → b C B A C C B ∣ a A C C B ∣ b C B C ′ A C C B ∣ a C ′ A C C B ∣ b C B A C C B C ′ ∣ a A C C B C ′ ∣ b C B C ′ A C C B C ′ ∣ a C ′ A C C B C ′
其實C ′ C' C ′ 只是我寫出來嚇唬人的,用來說明GNF範式可能很長。
5.13 下推自動機
下推自動機(PDA)比有限自動機多了一個下推棧,所以可以記憶一個變量。其實如果把有限自動機改成無限也可以,可惜改不得。
組成 :一個有限狀態控制器,一個輸入帶,一個無限容量 的下推棧。
定義 :七元組M = ( Q , T , Γ , δ , q 0 , z 0 , F ) M=(Q,T,\Gamma,\delta,q_0,z_0,F) M = ( Q , T , Γ , δ , q 0 , z 0 , F )
和有限自動機相比,多了一個Γ \Gamma Γ :有限下推棧字母表
多了一個棧底符號z 0 z_0 z 0 ,通常其不包含在T T T 中,只是在PDA中用於表示棧底。
δ \delta δ 變成了Q × ( T ∪ ϵ ) × Γ → Q × Γ ∗ Q\times (T\cup \epsilon) \times \Gamma \to Q\times \Gamma^* Q × ( T ∪ ϵ ) × Γ → Q × Γ ∗ ,即根據當前狀態、帶上的輸入字符、棧頂符號或空(只有一個,不能是串)三者決定下一個狀態和棧頂的 字符串(壓入時可以壓入多個字符) 。
δ ( q , a , Z ) = { p , α } \delta(q,a,Z)=\{p,\alpha\} δ ( q , a , Z ) = { p , α } 表示,當自動機狀態爲q q q ,輸入符號爲a a a ,棧頂爲Z Z Z 時,將狀態轉換爲p p p ,在棧頂壓入α \alpha α 串。
格局 :用三元組( q , ω , α ) (q,\omega,\alpha) ( q , ω , α ) 描述。
q q q 當前狀態。
ω \omega ω 待輸入串,ω = ϵ \omega=\epsilon ω = ϵ 時表示輸入字符讀完。
α \alpha α 下推棧的內容,α \alpha α 從左到右是棧頂到棧底,α = ϵ \alpha=\epsilon α = ϵ 表示棧空。
接受方式 :
終態接受:輸入結束時狀態處於接受狀態。即推導過程是( q 0 , ω , z 0 ) ├ ∗ ( q , ϵ , α ) , q ∈ F (q_0,\omega,z_0)├^*(q,\epsilon,\alpha),q\in F ( q 0 , ω , z 0 ) ├ ∗ ( q , ϵ , α ) , q ∈ F
空棧接受:輸入結束時下推棧爲空。即推導過程是( q 0 , ω , z 0 ) ├ ∗ ( q , ϵ , ϵ ) (q_0,\omega,z_0)├^*(q,\epsilon,\epsilon) ( q 0 , ω , z 0 ) ├ ∗ ( q , ϵ , ϵ ) 。爲了標識空棧接受,一般把F F F 標記成∅ \empty ∅ .
DPDA :確定的下推自動機,即每一次轉移都只有一個選擇。
具體來說, 每次轉移必須符合下列的一種情況
∣ δ ( q , a , z ) ∣ = 1 ∧ δ ( q , ϵ , Z ) = ∅ |\delta(q,a,z)|=1∧ \delta(q,\epsilon,Z)=\empty ∣ δ ( q , a , z ) ∣ = 1 ∧ δ ( q , ϵ , Z ) = ∅
δ ( q , a , z ) = ∅ ∧ ∣ δ ( q , ϵ , z ) ∣ ≤ 1 \delta(q,a,z)=\empty∧ |\delta(q,\epsilon,z)|\le 1 δ ( q , a , z ) = ∅ ∧ ∣ δ ( q , ϵ , z ) ∣ ≤ 1
這樣可以防止在轉移時搞不清楚是用字符轉移還是用空轉移導致的不同結果。
NDPA :不確定的下推自動機。
5.14 PDA的設計
5.14.1 L ( M ) = { a n b n ∣ n ≥ 0 } L(M)=\{a^nb^n|n\ge 0\} L ( M ) = { a n b n ∣ n ≥ 0 }
思路 :把輸入的字符 a 入棧,當開始輸入 b 時,從棧中彈出 a ,若 a 、 b 個數相同,則到達終態,且棧中空。
5.15 終態接受↔ \leftrightarrow ↔ 空棧接受
5.15.1 終態接受👉空棧接受
精髓 :既然終態不在接受了,就把原來的終態空轉移到同一個狀態,然後把棧彈空。
方法 :M f ( Q , T , Γ , δ , q 0 , z 0 , F ) M_f(Q,T,\Gamma,\delta,q_0,z_0,F) M f ( Q , T , Γ , δ , q 0 , z 0 , F ) ,構造M ∅ = ( Q ∪ { q e , q 1 } , T , Γ ∪ { z 1 } , δ 1 , q 1 , z 1 , ∅ ) M_\empty=(Q\cup\{q_e,q_1\},T,\Gamma\cup \{z_1\},\delta_1,q_1,z_1,\empty) M ∅ = ( Q ∪ { q e , q 1 } , T , Γ ∪ { z 1 } , δ 1 , q 1 , z 1 , ∅ )
即增加一個起始狀態q 1 q_1 q 1 ,一個放空用狀態q e q_e q e ,同時把棧底符號換成z 1 z_1 z 1 (因爲z 0 z_0 z 0 也要被彈掉)。
δ 1 ( q 1 , ϵ , z 1 ) = { ( q 0 , z 0 z 1 ) } \delta_1(q_1,\epsilon,z_1)=\{(q_0,z_0z_1)\} δ 1 ( q 1 , ϵ , z 1 ) = { ( q 0 , z 0 z 1 ) } ,即從空棧PDA的起始狀態無條件進入原終態PDA。
δ 1 ( q , a , z ) = δ ( q , a , z ) \delta_1(q,a,z)=\delta(q,a,z) δ 1 ( q , a , z ) = δ ( q , a , z ) ,即空棧PDA繼承原終態PDA的所有轉移(含空轉移)。
∀ q f ∈ F , δ 1 ( q f , ϵ , z ) = { ( q e , ϵ ) } \forall q_f\in F,\delta_1(q_f,\epsilon,z)=\{(q_e,\epsilon)\} ∀ q f ∈ F , δ 1 ( q f , ϵ , z ) = { ( q e , ϵ ) } ,將終態無條件跳轉到放空用終態。
δ ( q e , ϵ , z ) = { ( q e , ϵ ) } \delta(q_e,\epsilon,z)=\{(q_e,\epsilon)\} δ ( q e , ϵ , z ) = { ( q e , ϵ ) } 即自己把棧彈空。
5.15.2 空棧接受👉終態接受
精髓 :更簡單,把所有的狀態都在空棧時轉移到同一個可接受狀態即可。
方法 :M ∅ ( Q , T , Γ , δ ∅ , q 0 , z 0 , ∅ ) M_\empty(Q,T,\Gamma,\delta_\empty,q_0,z_0,\empty) M ∅ ( Q , T , Γ , δ ∅ , q 0 , z 0 , ∅ ) ,構造M f = ( Q ∪ { q 1 , q f } , T , Γ ∪ { z 1 } , δ f , q 1 , z 1 , { q f } ) M_f=(Q\cup\{q_1,q_f\},T,\Gamma\cup\{z_1\},\delta_f,q_1,z_1,\{q_f\}) M f = ( Q ∪ { q 1 , q f } , T , Γ ∪ { z 1 } , δ f , q 1 , z 1 , { q f } )
δ ( q 1 , ϵ , z 1 ) = { ( q 0 , z 0 z 1 ) } \delta(q_1,\epsilon,z_1)=\{(q_0,z_0z_1)\} δ ( q 1 , ϵ , z 1 ) = { ( q 0 , z 0 z 1 ) } ,即把空棧PDA的棧底符號壓進去,然後轉移到其開始狀態。
δ f ( q , a , z ) = δ ∅ ( q , a , z ) \delta_f(q,a,z)=\delta_\empty (q,a,z) δ f ( q , a , z ) = δ ∅ ( q , a , z ) ,即終態PDA繼承原空棧PDA的所有轉移(含空轉移)。
δ f ( q , ϵ , z 1 ) = { ( q f , ϵ ) } \delta_f(q,\epsilon,z_1)=\{(q_f,\epsilon)\} δ f ( q , ϵ , z 1 ) = { ( q f , ϵ ) } ,即當任何一個狀態處出現空棧(z 0 z_0 z 0 沒了。露出了z 1 z_1 z 1 ),就跳轉到一個公共的接受狀態。
5.16 上下文無關文法↔ \leftrightarrow ↔ 下推自動機
5.16.1 上下文無關文法👉下推自動機
精髓 :挺無聊的,總共只有一個狀態。把文法的符號作爲棧符號,開始壓入S,如果棧頂是非終結符就做最左推導,如果棧頂是終結符就等輸入來把它喫掉,最後棧空了就接受了。
方法 :對於C F G G = ( N , T , P , S ) CFG\ G=(N,T,P,S) C F G G = ( N , T , P , S ) ,構造P D A M = ( Q , T , Γ , δ , q 0 , z 0 , F ) PDA\ M=(Q,T,\Gamma,\delta,q_0,z_0,F) P D A M = ( Q , T , Γ , δ , q 0 , z 0 , F )
其中Q Q Q 只需要一個狀態,Q = { q } Q=\{q\} Q = { q }
T T T 是可以輸入的非終結符
Γ = N ∪ T \Gamma=N\cup T Γ = N ∪ T ,即棧內的符號可以是非終結符和終結符。
q 0 = q q_0=q q 0 = q ,反正只有一個狀態。
z 0 = S z_0=S z 0 = S ,從起始符開始推導。
F = ∅ F=\empty F = ∅ ,空棧接受
δ \delta δ 如下
對於A ∈ N A\in N A ∈ N ,增加δ ( q , ϵ , A ) = { ( q , β ) ∣ i f A → β ∈ P } \delta(q,\epsilon,A)=\{(q,\beta)|if\ A\to \beta\in P\} δ ( q , ϵ , A ) = { ( q , β ) ∣ i f A → β ∈ P } ,即PDA在棧頂遇到非終結符就無條件做推導。
對於a ∈ T , δ ( q , a , a ) = { ( q , ϵ ) } a\in T,\delta(q,a,a)=\{(q,\epsilon)\} a ∈ T , δ ( q , a , a ) = { ( q , ϵ ) } ,即PDA遇到棧頂遇到終結符就等待輸入相同的字符後把它喫掉。
這個很重要,容易忘 。
5.16.2 下推自動機👉上下文無關文法
5.17 CFL的泵引理
和正則語言的泵引理類似,都是說明,如果語言的句子長於一個常數時,總是有子串在重複。不過正則語言是一個子串重複,CFL是兩個。
定理 :C F L L CFL\ L C F L L 一定存在常數N N N ,對於ω ∈ L ∧ ∣ ω ∣ ≥ N \omega \in L ∧ |\omega|\ge N ω ∈ L ∧ ∣ ω ∣ ≥ N ,則存在分解ω = ω 1 ω 2 ω 0 ω 3 ω 4 \omega=\omega_1\omega_2\omega_0\omega_3\omega_4 ω = ω 1 ω 2 ω 0 ω 3 ω 4 ,並且ω 2 ω 3 ≠ ϵ , ∣ ω 2 ω 0 ω 3 ∣ ≤ N \omega_2\omega_3\neq \epsilon,|\omega_2\omega_0\omega_3|\le N ω 2 ω 3 = ϵ , ∣ ω 2 ω 0 ω 3 ∣ ≤ N (這很重要),對於∀ i ≥ 0 , ω 1 ω 2 i ω 0 ω 3 i ω 4 ∈ L \forall i\ge0,\omega_1\omega_2^i\omega_0\omega_3^i\omega_4\in L ∀ i ≥ 0 , ω 1 ω 2 i ω 0 ω 3 i ω 4 ∈ L .(L = ϵ L={\epsilon} L = ϵ 除外)
原因 :通俗一點解釋,證明不考。對於語言L L L ,設ϵ ∉ L \epsilon \notin L ϵ ∈ / L ,其Chomsky文法爲G G G .設某個句子ω ∈ L \omega\in L ω ∈ L ,其使用文法G G G 的推導樹是一顆二叉樹,設該二叉樹的路徑長度(從根節點到葉節點的最大長度)爲n n n ,則邊緣長度∣ ω ∣ ≤ 2 n − 1 |\omega|\le 2^{n-1} ∣ ω ∣ ≤ 2 n − 1 (滿二叉樹時取等)。
如果說文法G G G 內有n n n 個非終結符,那麼,當∣ ω ∣ ≥ 2 n = N |\omega|\ge 2^n=N ∣ ω ∣ ≥ 2 n = N 時,根據Pigeonhole定理,一定有兩個節點(途徑非終結符)相同,那麼,這兩個節點,除了重複自己外還會重複另一個分支。
舉個例子,有三個終結符{ A , B , C } \{A,B,C\} { A , B , C }
然後A->C->B又會推出A,不斷重複,此時A的另一個分支α \alpha α 不斷重複,產生了ω 3 i \omega_3^i ω 3 i
如果把C和α \alpha α 左右子樹的位置對調一下
此時產生的是ω 2 i \omega_2^i ω 2 i .
由於我們不能確定α \alpha α 在左子樹還是右子樹,或者兩邊都有遞歸。不妨兩邊都用,大不了ω 2 = ϵ \omega_2=\epsilon ω 2 = ϵ 或者ω 3 = ϵ \omega_3=\epsilon ω 3 = ϵ ,但是兩者不能同時爲空。
5.17.1 L = { a n b n c n ∣ n ≥ 1 } i s n o t C F L . L=\{a^nb^nc^n|n\ge 1\}\ is\ not\ CFL. L = { a n b n c n ∣ n ≥ 1 } i s n o t C F L .
假設L L L 是C F L CFL C F L ,則知存在N N N ,對於任意ω ≥ N \omega\ge N ω ≥ N 滿足泵引理。
不妨取ω = a N b N c N = ω 1 ω 2 ω 0 ω 3 ω 4 \omega =a^Nb^Nc^N=\omega_1\omega_2\omega_0\omega_3\omega_4 ω = a N b N c N = ω 1 ω 2 ω 0 ω 3 ω 4 ,考慮ω 2 ω 0 ω 3 \omega_2\omega_0\omega_3 ω 2 ω 0 ω 3 的位置,無非有兩種情況:
若a ∈ ω 2 ∧ c ∈ ω 3 a\in \omega_2∧ c\in \omega_3 a ∈ ω 2 ∧ c ∈ ω 3 ,則因爲要跨過全b b b ,∣ ω 2 ω 0 ω 3 ∣ ≥ 1 + N + 1 = N + 2 |\omega_2\omega_0\omega_3|\ge 1+N+1=N+2 ∣ ω 2 ω 0 ω 3 ∣ ≥ 1 + N + 1 = N + 2 ,但是泵引理要求∣ ω 2 ω 0 ω 3 ∣ ≤ N |\omega_2\omega_0\omega_3|\le N ∣ ω 2 ω 0 ω 3 ∣ ≤ N ,所以不成立。
因爲∣ ω 2 ω 0 ω 3 ∣ ≤ N |\omega_2\omega_0\omega_3|\le N ∣ ω 2 ω 0 ω 3 ∣ ≤ N ,所以在滑動時,ω 2 \omega_2 ω 2 和ω 3 \omega_3 ω 3 可能會含有相同字符。這裏以a a a 爲例。
ω 2 = a m , ω 0 = a l , ω 3 = a j b k \omega_2=a^m,\omega_0=a^l,\omega_3=a^jb^k ω 2 = a m , ω 0 = a l , ω 3 = a j b k ,由於ω 0 \omega_0 ω 0 不可以重複而ω 2 , ω 3 \omega_2,\omega_3 ω 2 , ω 3 可以重複,取ω ′ = ω 1 ω 2 2 ω 0 ω 3 2 ω 4 \omega'=\omega_1\omega_2^2\omega_0\omega_3^2\omega_4 ω ′ = ω 1 ω 2 2 ω 0 ω 3 2 ω 4 ,則此時a a a 的數量多於b b b 和c c c ,不屬於L L L 。
ω 2 \omega_2 ω 2 和ω 3 \omega_3 ω 3 不含有相同字符,這個更離譜。
綜上,所有可能給的情況下都與L L L 是C F L CFL C F L 的假設矛盾,所以L L L 不是C F L CFL C F L .
5.18 CFL的封閉性