JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

從字串到語言

一條字串只是一行有限的符號。把字串收進一個集合,你就得到一個語言——這門學科其實一直暗暗圍繞著的那個物件,也是從「問題」通往「機器」的橋樑。

從一條字串到全部字串:Sigma-star

在上一篇你已經確定了單一字串的意思:一段從字母表 Sigma(希臘字母 sigma,我們固定的允許符號袋)取出的、有限且有序的符號串。你也認識了字串長度空字串 epsilon(長度為零的那條唯一字串),以及串接(把兩條字串首尾黏在一起)。現在我們踏出一個看似微小、卻會重整整門學科的一步:不再一次只看一條字串,而是去看字串的集合

首先,替「最大的那個集合」取個名字。我們用 Sigma-star(常寫作 Σ*,那顆小星星叫做 Kleene 星號)來表示在 Sigma 上能造出的所有有限字串所成的集合——包含 epsilon 在內。若 Sigma = {a, b},則 Sigma-star 從 epsilon、a、b、aa、ab、ba、bb、aaa、… 開始,永遠列不完。請誠實記住兩件事:Sigma-star 裡的每一條字串都是有限的,但 Sigma-star 這個集合本身卻是無限的(只要 Sigma 至少有一個符號)。沒有「最長的字串」,就如同沒有「最大的整數」一樣。

語言就只是一個字串的集合

接下來是後續整道階梯所立足的定義,而它樸素到令人吃驚。在 Sigma 上的一個(形式)語言 L,就是 Sigma-star 的任意一個子集合。就這樣而已:語言是一個集合,只不過它的成員恰好都是字串。它可以是有限的(像 {a, ab, abc})、無限的(像「所有偶數長度的字串」),甚至可以是空的。這裡還沒有文法、沒有意義、也還沒有機器——就只是從 Sigma-star 這個巨大的池子裡,挑出來的一堆字串。

既然語言是一個集合,我們就用描述任何集合的方式來描述它。集合小的時候可以一一列舉,不小的時候就用集合建構式記法。舉例來說,在 Sigma = {a, b} 上,「a 的個數為偶數的字串」這個語言就是 { w 屬於 Sigma-star :w 中 a 的個數為偶 }。這短短一句描述悄悄裝進了 epsilon(零個 a 也算偶數)、b、bb、aa、aba、baab,以及無窮多條其他字串。判斷哪些字串屬於、哪些不屬於,正是你在接下來整道階梯中要磨練的功夫。

空語言不是「只含空字串的語言」

接下來是一個幾乎每個人第一次都會被絆倒、值得放慢腳步看清楚的區別:空語言,與只含空字串的語言空語言寫作 {} 或代表「空」的集合符號,它完全沒有任何成員——零條字串。而epsilon-語言 {epsilon} 恰好有個成員,就是那條空字串。兩者在紙上看起來幾乎一模一樣,但一個是空盒子,另一個則是裝著一張長度為零的小紙條的盒子。

這個差別不是吹毛求疵——它會改變答案。空語言的大小是 0;epsilon-語言的大小是 1。問「這個語言接受空輸入嗎?」,空語言說不,而 {epsilon} 說是。等到機器登場時,空語言正是一台沒有任何接受狀態DFA 所識別的語言,而 {epsilon} 則由一台「只在還沒讀任何東西之前就接受」的機器所識別。紙上長得一樣,行為卻完全相反。

用舊語言造出新語言

既然語言是集合,所有常見的集合運算都原封不動適用:聯集(L1 聯集 L2 = 屬於任一者的字串)、交集(同時屬於兩者)、補集(在 Sigma-star 中而在 L 內的字串)。但語言還繼承了一般集合所沒有的、帶字串味道的運算。其中最重要的是語言串接:L1 . L2 是「從 L1 取一條字串、把 L2 的一條字串黏在它尾端」所能造出的所有字串所成的集合。例如 {a, ab} . {c, d} = {ac, ad, abc, abd}。

皇冠上的明珠是作用在語言上的 Kleene 星號,寫作 L*。它的意思是:從 L 中取零條或多條字串,以任意組合、允許重複地串接起來。關鍵在於「取零條」是合法的,所以 epsilon 永遠在 L* 裡(即使 L 本身不含 epsilon)。若 L = {ab},則 L* = {epsilon, ab, abab, ababab, …}。本級的下一篇會完整地專講 L* 與這些運算;這裡先感受它的樣貌就好。注意 Sigma-star 恰好就是把這個構造套在 L = Sigma 上:對字母表本身打星號。

Sigma = {a, b}

L1 = { a, ab }          L2 = { c, d }
L1 union L2  = { a, ab, c, d }
L1 . L2      = { ac, ad, abc, abd }

L = { ab }
L*  = { epsilon, ab, abab, ababab, ... }   (zero or more copies; note epsilon)
L+  = { ab, abab, ababab, ... }             (one or more copies; no epsilon)
幾個運算並排比較。星號永遠包含 epsilon(零份),而加號永遠不包含。

為何重要:一個問題「就是」一個語言

接下來是讓整套裝置師出有名的關鍵一步,它也回答了本級第 1 篇提出的「為什麼要抽象地研究計算?」。幾乎任何是非式的計算問題,都能被編碼成一個語言。先替輸入挑一種合理的字串編碼;接著把所有「答案為」的已編碼輸入,恰好收集成一個語言 L。於是「輸入 w 的答案是不是『是』?」就變成了成員資格問題:字串 w 是不是 L 的成員?而把這個答案算出來,正是 L 的判定問題

一個具體例子:「這個數是質數嗎?」變成語言 PRIMES = { w :w 是某個質數的二進位編碼 }。那麼一台判定質數性的機器,恰好就是一台判定「是否屬於 PRIMES」的機器。這種重新框定是這個領域的看家本領:它把天差地遠的各種問題——排序、圖的連通性、定理檢查——統統化成「w 是否屬於 L?」這唯一一種統一的形狀。一旦每個問題都是一個語言,我們就能問:到底(若存在的話)是哪一台機器能識別它。

而這個問題有一張著名的地圖。語言會依「你需要多複雜的機器才能識別它」分層,排進喬姆斯基階層正規語言(由有限自動機識別,下一級的主題)包在上下文無關語言(由下推自動機識別——一台有限機器再加一個堆疊)之中,又包在遞迴可枚舉語言(由圖靈機識別,最強大的模型)之中。每一層都嚴格地包在下一層裡面。攀爬之前先誠實提醒一句:「正規」不等於「有限」——無限的語言 Sigma-star 本身就是正規的。從某種意義上說,這道階梯接下來的全部內容,就是一層一層去探索這張地圖。