在結束這段關於電腦的插曲之前,丹尼特(Daniel Dennett)想再引入一組有用的概念:原始碼(source code)註解(comments)目的碼(object code)。這些概念對於後續理解「意義如何棲身於大腦中」有重要應用。

處理難題之前,先仔細研究一個簡單到不能再簡單的例子,往往是明智的做法——先在小事上把概念抓穩,再去挑戰真正燒腦的問題。

在人工智慧領域,這類刻意簡化的例子有個好聽的名字:玩具問題(toy problems)。先解掉玩具問題,再去對付棘手的真實世界大問題。

以下是一個為了簡化而虛構、但其餘部分都相當寫實的故事:人類電梯操作員是如何被電腦晶片取代的。

從人類操作員到規則書#

在丹尼特年輕時,電梯裡有專職操作員,工作就是整天上上下下、停在正確樓層讓乘客進出。早期他們要操作一個古怪的把手,順時針或逆時針轉動來讓電梯升降,還得靠技巧把電梯停在恰好對齊的高度。

  • 乘客進出時常得往上或往下跨一兩吋,操作員總會提醒大家小心。
  • 他們有一大堆規則:什麼時候該說什麼、先去哪一層、怎麼開門等等。
  • 訓練的內容就是背熟規則,然後不斷練習,直到照規則行動成為第二天性。

這套規則是多年設計過程中經過無數微調與改良而逐漸成形的。假設這個過程大致已經定案,產出了一本理想規則書。它運作得極好:任何人只要完全照規則走,就是一位優秀的電梯操作員。

程式設計師接手規則書#

現在想像一下:當一個簡單的電腦程式有能力接手操作員的所有控制任務時,會發生什麼事。

現實中這是漸進發生的——各種自動機械裝置陸續把較需技巧的任務從操作員手中接走。但這裡我們假設電梯是「一步到位」,直接從人類操作員跳到全電腦控制。

電梯製造商找來一組軟體工程師,把人類操作員一直遵循的規則書交給他們:「這是我們想要的性能規格,寫一個能像最好的操作員一樣遵守書中所有規則的程式,我們就滿意了。」

程式設計師逐條讀規則書,列出所有必須採取的動作,以及這些動作被規定或被禁止的條件。過程中他們可以順手清理規則書裡的雜亂之處:

  • 例如裝上感測器確保電梯永遠停在完全對齊的高度後,就能刪掉那條要操作員喊「請往上一步/請往下一步」的規則。
  • 但可能保留一段簡單的錄音:「◯ 樓到了,請小心腳步。」

接著他們寫出程式草稿,用的通常是所謂的虛擬碼(pseudo-code)——一種介於日常人類語言與更嚴格的原始碼之間的混種語言。一行虛擬碼可能長這樣:

if callfloor > currentfloor, then
    ASCEND until callfloor = currentfloor and STOP;
    OPENDOOR. WAIT. . . .

原始碼、註解與編譯器#

一旦虛擬碼裡的計畫清楚了、看起來確實是想要的東西,就能把它翻譯成原始碼——一套嚴謹得多、結構化得多的操作系統,包含各種術語定義:變數、副程式等等。

人類仍能相當輕鬆地讀懂原始碼(畢竟原始碼是人寫的),所以規則書裡的規則與術語其實仍明白地表現在原始碼中,只要你知道怎麼找。有兩個特徵讓這件事更容易:

  • 變數與操作的命名通常直接把意思寫在臉上(如 callfloorweightsumTELLFLOOR)。
  • 程式設計師可以在原始碼中加入註解——寫給其他人類讀者看的括號說明,交代這段程式當初的意圖與各部分該做什麼。

寫程式時,隨手替自己加註解是明智的。你很容易忘記某行程式碼原本以為在做什麼;日後回頭修正錯誤時,這些註解非常有用。

原始碼必須依嚴格語法仔細撰寫,每個元素都在對的位置、所有標點都在對的順序,因為它要餵給編譯器(compiler)。編譯器把原始碼翻譯成機器(或虛擬機器)能執行的一連串基本操作,也就是目的碼

編譯器不能被要求去「猜」程式設計師的意思;原始碼必須告訴編譯器確切要執行哪些操作。不過,執行這些任務的方式可以有很多種,編譯器會依情況找出有效率的做法。

有些編譯器就是比別的好。把同一份原始碼餵給兩個不同編譯器,其中一個產出的目的碼可能跑得明顯比另一個快。

延伸案例:兩個編譯器對弈的西洋棋程式

想像你寫了一個西洋棋程式,把它的原始碼餵給兩個不同的編譯器,再讓這兩個編譯版本在同一台電腦上對弈。

儘管兩個版本「以相同順序思考完全相同的念頭」(它們必須如此——原始碼一模一樣),其中一個卻可能總是贏,只因為它想得更快:用更少的基本機器週期,因而在有限時間內能往前多推算幾步棋。

燒進晶片,取代一個真人#

編譯器產出目的碼後,就能由(虛擬)機器執行——它是一個可執行檔,檔名通常帶著「.exe」後綴。它可能需要好幾輪除錯(回到原始碼調整、再重新編譯……),但最終會成為一個「完成」的產品。

這份目的碼可以被「燒錄」進一顆小晶片上的 ROM,晶片裡是一台萬用機器(Universal machine),上面還能疊加任意數量的虛擬機器。裝進電梯時,要把輸入端接上各種轉換器(按鈕訊號、地板量測乘客總重的秤、其他零件),再把輸出端接上作動器(開關門、升降車廂的馬達、更新顯示、播放錄音)。

如此,一台機器取代了一個真實的人類——不是比喻性的小矮人(homunculus)。而且這台機器遵循和人類操作員相同的規則。

「差不多」遵循規則的中間地帶#

它真的遵循相同規則嗎?真的嗎?好吧,其實不是。它是差不多(sorta) 遵循相同的規則。

這是一個很好的中間案例,介於兩個極端之間:

  • 一端是背熟規則的人類——她在心智中確實表徵了、並會查閱那些支配她行為的規則。
  • 另一端是行星——它們的軌道被方程式優雅地描述,行星「遵守」這些方程式,但它們心中並沒有任何規則。

我們人類也常常處在這個中間層。當我們透過練習把一套明確規則內化或例行化之後,可能就把規則本身丟掉、甚至忘掉了(例如英文拼字口訣「i 在 e 之前,除非跟在 c 之後……」)。

更進一步,我們甚至能「差不多遵循」那些至今仍未被明確、除錯完成的規則——例如英文文法規則,它至今仍讓語言學家傷透腦筋。

換成本例的說法:今天的語言學家還在苦苦掙扎,想寫出「說好英語」的規則書;而每個十歲的英語母語者,卻早已在腦中安裝並除錯了一套相當不錯的「英語虛擬機(EVM)」目的碼。

丹尼特的同事雷·傑肯道夫(Ray Jackendoff, 1993)稱此為語言學習的悖論(paradox of language learning):孩童毫不費力地吸收並遵循文法規則,而專業語言學家卻還在琢磨該怎麼把這些規則表達出來。

大腦是「未加註解的程式碼」#

在離開這個例子前,注意一件事:原始碼中那些幫設計者追蹤各部分用途的註解,在打造我們大腦的設計過程中完全沒有對應物

當天擇(natural selection)在我們腦中安裝各種功能結構時,這就像是未加註解的程式碼——它存在是有原因的,但那個原因並沒有以任何標籤或說明表徵在結構之中(反正大腦也讀不懂)。

發育與學習過程中的種種調整,同樣有著未被註解、未被宣告的理由。我們就像那些語言學家一樣,仍在努力逆向工程這一切「規則」與「程序」。

這項任務比「為了還原原始碼(扣掉註解)而逆向工程目的碼」還要更難——但原則上,它是做得到的。