上下文無關語言與正規語言的交集(intersection with a regular language)
兩個 CFL 取交集可能衝出家族,因為那要一個堆疊做兩件事。但把一個上下文無關語言和一個「正規」語言混在一起,故事就又友善了:交集 L ∩ R,其中 L 是上下文無關、R 是正規語言,「永遠」是上下文無關。直覺是正規語言根本不需要堆疊——有限自動機只有有限記憶——所以把它的記帳加到 PDA 上,對 PDA 不花額外成本;那一個堆疊仍然完全留給上下文無關的部分。
這個構造是一種積構造(product),就像兩台有限自動機的那種,只是套用在一台 PDA 與一台 DFA 上。讓辨識 L 的 PDA 和辨識 R 的 DFA 在同一段輸入上並排、同步地跑。建一台新 PDA,其狀態是一對 (PDA 狀態, DFA 狀態);它的單一堆疊就是 PDA 的堆疊,DFA 那一半碰都不碰。讀到一個符號時,PDA 那一半做它的堆疊動作(推入或彈出),DFA 那一半只更新它的有限狀態。只有「兩」半都接受時才接受。因為 DFA 不貢獻任何堆疊,一個堆疊仍然夠用——所以結果是一台貨真價實的 PDA,因此其語言是上下文無關。
這是整個領域裡最有用的工具之一,無論是設計還是證明都用得上。設計上,你可以用一個正規濾鏡把上下文無關語言削減——例如只保留「同時」是偶數長度的括號平衡字串。證明上,它是顯示某語言「不」是上下文無關的主力:若你懷疑 L 不是上下文無關,就把它和一個精心挑選的正規語言 R 取交集,逼出一個更簡單的核心(常是像 a^n b^n c^n 這樣的東西),再對那個核心套用幫浦引理。若 L 是上下文無關,那 L ∩ R 也必須是上下文無關——而證明那個核心「不」是,就給出乾淨的矛盾。
設 L = { w : w 中 a 與 b 的個數相等 },它是上下文無關,R = a*b*(正規語言)。則 L ∩ R 只保留「先 a 後 b 且個數相等」的字串:恰好是 { a^n b^n },仍是上下文無關。反過來,把一個被懷疑為非 CFL 的語言與一個正規語言取交集以隔離出 a^n b^n c^n,正是許多非上下文無關性證明的動力。
CFL ∩ 正規語言永遠是 CFL——一台 PDA 與一台 DFA 的積,只用到 PDA 那一個堆疊。
這條封閉性是專門針對與「正規」語言取交集。把一個 CFL 與另一個 CFL 取交集「並不」安全——正是 R 的正規性讓那一個堆疊保持空閒。