電腦擁有的力量,在幾百年前看來簡直像奇蹟,像「真正的魔法」。然而,儘管許多電腦程式複雜得令人卻步,它們全都由完全能用最簡單詞彙解釋清楚的步驟組成。電腦的作為裡沒有神秘可言——而這正是電腦作為思考工具的價值所在。本章的任務,就是把這層「魔法」去神秘化。
為什麼要拆解電腦#
我們會從一台大概是所能想像最簡單的電腦——暫存器機(register machine)——出發,看看它到底有什麼能耐、又是為什麼。接著會看到**圖靈機(Turing machine)與馮諾伊曼機(Von Neumann machine,例如你的筆電)**其實只是更有效率的暫存器機。凡是你筆電做得到的,暫存器機都做得到——只不過別憋著氣等,它可能得花上好幾個世紀。
丹尼特(Daniel Dennett)在此提出的問題是:如果人腦真是一台巨大的電腦,那麼它的一切活動就都能被理解、不留任何殘餘的神秘——只要我們找得到方法。
這套方法就是逆向工程(reverse engineering):研究一個複雜系統,揭開它「怎麼做到它所做之事」。逆向工程告訴我們心臟如何作為幫浦運作、肺如何吸氧排碳;神經科學則是對大腦的逆向工程嘗試。
這是一個爭議激烈的題目。小說家湯姆.沃爾夫(Tom Wolfe)用一篇文章的標題精準戳中了戰火核心:〈抱歉,但你的靈魂剛剛死了〉。若要探索這片危險地帶,而不只是浪費大家時間在宣言與譴責上,我們需要更銳利的工具。
要證明大腦「不是、也不可能是」電腦,唯一令人滿意的做法是證明以下兩者之一:
- 它某些「運作零件」從事的資訊處理,是任何電腦都無法從事的;或
- 它的零件所從事的簡單活動,無法被以電腦方式組合、聚合、編排成我們熟知並珍視的心智壯舉。
不少專家(不只哲學家,還有神經科學家、心理學家、語言學家甚至物理學家)主張「電腦隱喻」誤導人心,甚至主張大腦能做電腦做不到的事。這類批評通常預設了一個非常天真的「電腦是什麼」的看法,最後只證明了一個顯而易見卻無關緊要的事實:大腦能做很多你筆電做不到的事(因為筆電的感測器與效應器少得可憐、記憶體貧乏、速度有限)。要評估這些關於電腦力量的強懷疑論,我們得先弄懂電腦的力量究竟從何而來。
暫存器機:只有三個動作#
暫存器機的絕妙構想,是在電腦時代黎明期由邏輯學家**王浩(Hao Wang)**提出的——順帶一提,他是哥德爾(Kurt Gödel)的學生,也是位哲學家。這是一件優雅的思考工具,你的工具箱裡應該要有它,只是它遠不如它應得的那樣有名。
一台暫存器機是理想化、想像出來(但完全可能存在)的電腦,只由兩樣東西組成:
- 一組(有限數目的)暫存器:每個是一個記憶位置,有唯一位址(暫存器 1、2、3……),內容是單一非負整數(0, 1, 2, 3…)。把每個暫存器想成一個大箱子,裡頭裝著 0 到任意多顆豆子。
- 一個處理單元:具備三種簡單能力,能一步一步、一次一個地「遵循」三條「指令」。
這三條指令(附上好記的簡稱)是:
- End:停止或關機。
- Inc(Increment,遞增):把暫存器 n 的內容加 1(往箱子 n 放一顆豆子),然後跳到另一步驟 m。
- Deb(Decrement-or-Branch,遞減或分支):若能減,把暫存器 n 的內容減 1,並跳到步驟 m;若不能減(因為內容已是 0,箱子空了,取不出豆子),就**分支(Branch)**到步驟 p。
Deb 之所以要有「分支」這個複雜設計,關鍵就在:暫存器不能存負整數,所以當內容為 0 時遞減會卡住。這個「卡住就跳別的地方」的機制,正是整台機器力量的來源。
第一眼看去,你或許不覺得這麼簡單的機器能做什麼有趣的事——它能做的無非是往箱子放豆子、或取豆子(取得到就取,取不到就跳去別的指令)。但事實上,它能計算任何電腦能計算的一切。
從加法開始#
假設我們要把暫存器 1 的內容加到暫存器 2。若暫存器 1 裝著 [3]、暫存器 2 裝著 [4],我們希望程式結束時暫存器 2 變成 [7]。用一種可稱為 **RAP(Register Assembly Programming,暫存器組合語言)**的簡單語言,程式如下:
program 1: ADD [1,2]前兩條指令形成一個簡單迴圈:不斷遞減暫存器 1、遞增暫存器 2,直到暫存器 1 清空——處理單元「察覺」到這件事,於是分支到第 3 步,令它停機。

圖 1:加法程式(program 1)的指令表——三步驟以 Deb、Inc、End 把暫存器 1 的內容加到暫存器 2
這裡有個關鍵事實:處理單元除了「內容為 0」這種情形之外,根本無從得知暫存器裡是什麼數。用豆子箱的比喻,處理單元是盲的,摸到箱子空了才知道它空了。但即便如此,只要餵它 program 1,它永遠能把暫存器 1 的內容(不管是幾)加到暫存器 2 的內容上,然後停機。
換個震撼的說法:暫存器機能完美地把兩個數相加,卻根本不知道自己在加哪兩個數(甚至不知道數是什麼、加法是什麼)。
我們可以用流程圖(flow graph)來描繪這個過程:每個圓圈代表一條指令,圓圈裡的數字是要操作的暫存器位址(不是內容),「+」代表 Inc、「–」代表 Deb。程式一律從 α(alpha)開始,抵達 Ω(omega)時停止。注意每個 Deb 指令都有兩條出箭頭——一條給「能遞減時往哪走」,一條給「內容為 0、不能遞減時往哪走」。

圖 2:加法程式的流程圖——從 α 進入,暫存器 1 遞減(−1)、暫存器 2 遞增(+2)反覆循環,內容為 0 時分支到 Ω 停機
延伸:移動、複製、歸零三個基本操作
MOVE(搬移):把一個暫存器的內容搬到另一個。
program 2: MOVE [4,5]
圖 3:MOVE(搬移)程式的指令表——先清空暫存器 5,再把暫存器 4 的內容逐一搬入

圖 4:MOVE(搬移)程式的流程圖——先以 −5 迴圈歸零暫存器 5,再將暫存器 4 遞減(−4)、暫存器 5 遞增(+5),內容為 0 時分支到 Ω
這個程式的第一個迴圈先清空暫存器 5,免得它原有的內容污染稍後要建立的結果;第二個迴圈就是我們的加法迴圈,把暫存器 4 的內容加到已歸零的暫存器 5。這個初始化步驟叫歸零(zeroing out),是極常用的標準操作,你會不斷用它來為暫存器做好使用前準備。
COPY(複製):把內容複製到另一暫存器,同時保留原內容不變。
program 3: COPY [1,3]
圖 5:COPY(複製)程式的流程圖——透過歸零與備份迴圈,把暫存器 1 的內容複製到暫存器 3,同時保留原內容不變

圖 6:COPY(複製)程式的指令表(八步)
這是相當迂迴的複製法:先把暫存器 1 的內容搬到暫存器 3,同時在暫存器 4 做一份備份,再把備份搬回暫存器 1。但它永遠有效——無論起始內容為何,程式停機時暫存器 1 內容原封不動,暫存器 3 得到一份複本。
若你還看不透原理,拿幾個杯子當暫存器(在杯身寫上位址)、一堆硬幣當豆子,親手「手動模擬」整個流程。務必把這些基本流程內化到不必費神就能想通——後面我們要大量運用這項新技能。花幾分鐘讓自己「變成一台暫存器機」,就像演員化身哈姆雷特。
常見錯誤:學生以為遞減暫存器 n 時,取出的那顆硬幣得放進「別的暫存器」。不用。遞減掉的硬幣就丟回你那堆「無限供應」的大硬幣堆裡。
非破壞性加法:program 1 雖然把答案放進暫存器 2,卻在過程中摧毀了暫存器 1、2 的原始內容。若想保留這些值、把答案另放他處,可寫一支十三步的程式:先把答案暫存器 3 與一個當緩衝區的備用暫存器 4 歸零,透過複製與搬移把兩個加數各自累加進暫存器 3,最後緩衝區清空、答案在暫存器 3、兩個加數回到原位。
program 4: Non-destructive ADD [1,2,3]
圖 7:非破壞性加法的流程圖(十三步)——保留兩個加數的原值,把答案另存到暫存器 3

圖 8:非破壞性加法的指令表(十三步)
丹尼特提到一件輔助工具 RodRego:一台可下載的暫存器機模擬器(PC 與 Mac 版皆有,二十多年前於塔夫茨大學 Curricular Software Studio 開發)。你可以輸入 RAP 程式、看它一步步跑,用豆子或數字顯示暫存器內容,還有動畫展示處理單元在流程圖上走過的路徑。

圖 9:RodRego 暫存器機模擬器的畫面——以豆子箱顯示各暫存器內容,按任意鍵開始執行
減法、乘法、除法:都是加減的組合#
減法比加法棘手一點。若要用暫存器 1 減暫存器 2,麻煩在於:當暫存器 2 比暫存器 1 大時,暫存器 1 會在減法迴圈中途「歸零」,還沒減完就卡住。這時不能直接令機器停止(那會留下錯誤答案 0)。解法是利用這個歸零觸發新流程:倒退半個迴圈、撤銷對暫存器 2 的暫時遞減,此時暫存器 2 的內容若解讀為負數,正是正確答案。於是我們保留一個暫存器(例如暫存器 3)當正負號旗標,0 代表 +、1 代表 –。

圖 10:減法程式的流程圖——暫存器 1 減暫存器 2,途中暫存器 1 歸零時觸發修正流程

圖 11:減法程式的註解流程圖——初始化答案暫存器、初始化符號暫存器、相減、「修正」暫存器 2、放入負號旗標、把答案搬到暫存器 4
你可以在 RAP 程式裡用
#標記之間加入註解。註解是給你和其他人看的,RodRego 會忽略它們。
有了加減法,乘除就好辦了:
- 乘法:n 乘 m,就是把 n 加到自己身上 m 次。用一個暫存器當計數器,每完成一次加法迴圈就遞減一次,從 m 數到 0。
- 除法:把除數反覆從被除數中減去,數能減幾次。餘數留在特別的餘數暫存器。但除法必須加一道安全措施——不能除以 0。
除法前務必先檢查除數:試著遞減它。若能減,就再遞增一次(還原)後繼續;若一減就撞上 0,代表除數為 0,必須拉警報。做法是保留一個暫存器當 ERROR 旗標——例如暫存器 5 放 1,意思是「TILT!有人叫我除以 0!」

圖 12:除法程式的註解流程圖——檢查除數、除數為 0 時設錯誤旗標、還原除數、每成功減一次就遞增商
某個東西——例如暫存器機——能做完美的算術,卻完全不必理解自己在做什麼。
暫存器機不是心靈,它什麼也不理解;但它「算是」理解 Inc、Deb、End 這三件簡單的事——意思是每當這些指令出現,它就奴隸般地照做。它們當然不是「真正的」指令,只是「算是」指令:它們對我們看來像指令,機器也彷彿它們是指令般執行,所以稱之為指令再方便不過。
而 Deb(遞減或分支)正是暫存器機力量的鑰匙。它是唯一讓電腦能「察覺」(算是察覺)世界中某事、並用所察覺的來引導下一步的指令。事實上,這種**條件分支(conditional branching)**正是所有儲存程式電腦力量的關鍵——這一點早在十九世紀,愛達.勒芙蕾絲(Ada Lovelace)在她對查爾斯.巴貝奇(Charles Babbage)「分析機(Analytical Engine)」的傑出論述中就已認出。
延伸:愛達.勒芙蕾絲,第一位程式設計師
愛達.勒芙蕾絲是詩人拜倫勳爵(Lord Byron)的女兒,一位了不起的數學家。1843 年她翻譯了一篇義大利文對巴貝奇分析機的評註,並附上自己的註記——這些註記比原文更長、更深。註記中包含她精心設計、用巴貝奇分析機計算白努利數(Bernoulli numbers)的方法。因此她常被譽為史上第一位程式設計師。
一個數,可以代表任何東西#
把這些程式當零件組裝起來,一旦上手就變成頗為例行的工作。既然我們已經寫好各種算術程序,就能反覆重用它們。假設替它們編號:ADD 是運算 0、SUBTRACT 是 1、MULTIPLY 是 2……COPY 是 5、MOVE 是 6……那麼我們就能用一個暫存器來存放一條指令(用編號表示)。
例如做一台簡單口袋計算機:用暫存器 2 存運算(0=加、1=減、2=乘、3=除),把運算對象放進暫存器 1 與 3,於是「306」代表 3 + 6、「513」代表 5 – 3、「425」代表 4 × 5、「933」代表 9 ÷ 3。
在這個例子裡,暫存器內容(都是數字)被用來代表四種截然不同的東西:一個數、一種算術運算、一個數的正負號、一個錯誤旗標。
用已建好的積木,我們能組出更厲害的運算:SQUARING(平方)、FIND THE AVERAGE(求平均)、FACTOR(找因數)、COMPARE(比較)等等。特別有用的是 SEARCH:搜遍一百個暫存器,看有沒有哪個含有特定內容,並把該暫存器的位址放進另一個暫存器。
多虧 Deb 所體現的基本「感官」能力——它遞減暫存器時「察覺」到 0 的本事——我們能把暫存器機的「眼睛」轉向它自己,讓它檢視自身的暫存器,依所見來搬移內容、切換運算。
舉例而言,任何黑白圖片(包括這一頁的圖片)都能用一大排暫存器表示,一個暫存器對應一個像素,0 代表白點、1 代表黑點。於是你可以寫程式搜遍數千張圖片,找出「白底上一條水平黑直線」。把水平線辨識器、垂直線辨識器、半圓辨識器等幾十個辨別器串接起來,就能做出能辨認出(大寫)字母「A」的東西——而且是數百種不同字型!
這正是近代電腦程式的一項勝利:**光學字元辨識(OCR)**軟體,能掃描印刷頁面、相當可靠地轉成文字檔(每個字母或數字用 ASCII 碼的一個數表示,於是文字可搜尋,種種文書處理魔法都能靠純算術完成)。OCR 程式能「讀」嗎?並不真的能——它不理解眼前之物。它只是「算是」在讀,而這是一項極有用、可以加進我們豐富工具箱的能力。
一個數,可以是整支程式#
我們可以用暫存器裡的數代表指令(ADD、SUBTRACT、MOVE、SEARCH……)與位址,於是能把一整串指令存進一連串暫存器。假設有一支主程式(程式 A),指示機器逐一走過那些暫存器、照每個暫存器所指示去做,那麼我們就能在那些暫存器裡存放第二支程式 B。啟動機器跑程式 A,它做的第一件事就是去查那些告訴它「執行程式 B」的暫存器,於是照做。
這意味著:我們可以把程式 A 一勞永逸地燒進暫存器機中央處理單元的一組保留暫存器(可以是燒進 ROM 唯讀記憶體的「韌體」),然後用程式 A 去跑程式 B、C、D……端看我們在一般暫存器裡放什麼數。裝上程式 A,就把暫存器機變成了一台儲存程式電腦。
若我們設計一套讓指令無歧義的系統(例如規定每個指令名一律兩位數長),就能把構成程式 B 的整串數字,例如:
86, 92, 84, 29, 08, 50, 28, 54, 90, 28, 54, 90當成一個超大的長數字:
869284290850285490285490這個數字既是程式 B 獨一無二的「名字」,也就是程式本身。你筆電上存的程式——文書處理器、瀏覽器——就是這樣的長數字,動輒數百萬位(二進位)數字長。一支 10 MB 大小的程式,就是一串八千萬個 0 與 1。
圖靈機與通用機器#
**艾倫.圖靈(Alan Turing)**是想出這套方案的傑出理論家兼哲學家。他用的是另一台簡單的想像電腦:沿著一條分格的紙帶來回移動,依讀寫頭下方那一格讀到的是 0 還是 1(啊哈——又是條件分支!)來決定行為。圖靈機能做的,只有翻轉那個位元(把 0 擦成 1,或反之)或不動它,然後左移或右移一格、跳到下一條指令。
用只有 0 和 1、一次只動一格的圖靈機來寫加減法,比我們的暫存器機練習更令人卻步,但圖靈要表達的重點完全一樣。**通用圖靈機(Universal Turing machine)**就是一台裝了程式 A(愛硬接線也行)的裝置,能從紙帶「讀」出它的程式 B 並執行,把紙帶上其餘的東西當成程式 B 的資料或輸入。
王浩的暫存器機能執行任何可化約為算術與條件分支的程式,圖靈的圖靈機亦然。兩台機器都有那份美妙的力量:拿任何另一支程式的數字來執行它。
於是我們不必打造成千上萬台各自硬接線去做某項複雜任務的電腦,只需造一台通用機器(裝好程式 A),再餵它程式——軟體——來創造出各種虛擬機器(virtual machines),任我們差遣。
通用圖靈機是一台萬能模仿者,我們那台較不出名的通用暫存器機也是,你的筆電同樣是。你筆電能做的,通用暫存器機都能做,反之亦然。但別憋著氣等——沒人說所有機器速度相等。我們已看到暫存器機做除法(靠串列減法!)慢得令人心痛。
秘密六:後來的一切,都只是加速#
自圖靈以降的電腦史,恰恰就是「用越來越快的方式做暫存器機所做之事」的歷史——別的什麼也沒做。
**約翰.馮諾伊曼(John von Neumann)**創造了第一台真正可運作電腦的架構。為了加速,他把圖靈機一次一位元的讀寫頭「窗口」加寬成一次讀多位元。早期電腦讀 8、12、16 位元的「字組(word)」,今天普遍用 32 位元。這仍是個瓶頸——馮諾伊曼瓶頸(von Neumann bottleneck)——但已比圖靈機的瓶頸寬了三十二倍。
延伸:暫存器機與馮諾伊曼機的差異
字組會一次一個從記憶體複製進一個特別的暫存器(指令暫存器 Instruction Register),在那裡被讀取並執行。一個字組通常有兩部分:運算碼(Operation Code)(如 ADD、MULTIPLY、MOVE、COMPARE、JUMP-IF-ZERO)和一個位址(告訴電腦去哪個暫存器取要操作的內容)。
兩者最大差異在於:暫存器機能對任何暫存器動手(當然只有 Inc 和 Deb),而馮諾伊曼機把所有算術都在一個叫**累加器(Accumulator)**的地方完成,只負責把內容複製、搬移到構成記憶體的暫存器。它為這些額外搬運付出的代價,換來能執行許多不同的、各自硬接線的基本運算——ADD 有專屬電路、SUBTRACT 有另一個、JUMP-IF-ZERO 又是一個。運算碼就像電話系統的區碼、郵件的郵遞區號:把手上處理的東西送到正確地點執行。這就是軟體與硬體交會之處。
現代真實電腦有多少基本運算?可以有數百、數千條;也可以反璞歸真做成 RISC(精簡指令集電腦),只用幾十條基本運算,卻以驚人的執行速度彌補。(若 Inc 和 Deb 能比硬接線的 ADD 快跑一百萬倍,那麼像我們先前那樣用 Inc 和 Deb 拼出 ADD 反而划算——只要加法步數少於一百萬。)
現代電腦有多少暫存器?數百萬甚至數十億(但每個都有限,所以很大的數得攤在許多暫存器上)。一個位元組(byte)是 8 位元;若你有 64 MB 的 RAM,就有約一千六百萬個 32 位元暫存器。實數(如 √2 或 ⅓)用**浮點(floating point)**表示法儲存,把數拆成基數與指數兩部分,如同科學記號,讓電腦算術能處理(近似的)非自然數。
平行處理、大腦與速度極限#
若還嫌不夠快,就把許多台機器**平行(parallel)**串在一起同時工作,而非串列地排隊等結果。但要記得:平行機器能做的,純串列機器都能做,只是慢一些。事實上,近二十年被積極研究的平行機器,多半是在標準(非平行)馮諾伊曼機上模擬出來的虛擬機器。
丹尼特寫作時,日本富士通 K 電腦能達到 10.51 PETAFLOPS(每秒逾一萬兆次浮點運算)。
這或許差不多快到能即時模擬你大腦的計算活動。你的大腦是頂尖的平行處理器,約有一千億個神經元,每個都是有自己議程的複雜小主體。單是把視覺資訊從眼睛送往大腦的視「神經」,本身就有數百萬條通道(神經元)寬。
但神經元比電腦電路慢得多得多。一個神經元切換狀態、送出一個脈衝(大概就是它版本的 Inc 或 Deb)要花數毫秒——是千分之一秒,不是百萬分之一或十億分之一秒。電腦則以接近光速搬動位元,這也是為什麼把電腦造得更小是加速的關鍵:光走一英尺約需十億分之一秒,若要兩個程序通訊得比這更快,它們就得靠得比一英尺更近。
秘密七:沒有更多秘密了#
電腦最美妙的特色,或許就是:因為它們是由同樣死板簡單的零件(運算),透過簡單步驟一層層搭建起來的,所以根本沒有空間讓它們袖子裡藏任何秘密。
沒有靈質(ectoplasm)、沒有「型態共振」、沒有隱形力場、沒有迄今未知的物理定律、沒有神奇組織。你確知:只要你成功用電腦程式建模某個現象,模型裡起作用的因,除了那些由算術運算組成的因之外,別無其他。
那量子電腦呢?#
近來當紅的量子電腦,不是能做普通電腦做不到的事嗎?答案是又是又不是。它們能做的,是靠「量子疊加(quantum superposition)」同時解許多問題、同時算許多值。
但本質上,量子電腦只是速度上最新(也極其驚人)的一次革新——可謂處理速度的「量子躍進」。圖靈機在紙帶上蹣跚而行、暫存器機來回遞增遞減,在幾分鐘、幾小時、幾天這種實際小塊時間內能做的事有嚴格上限。像富士通 K 電腦這樣的超級電腦能把同樣的事做快幾兆倍,但對某些問題(尤其密碼學)仍不夠快。
那正是量子電腦的速度紅利可能派上用場之處——前提是人們能解開「打造一台穩定、實用的量子電腦」所遇到的極端困難的工程問題。這也許辦不到,那樣的話,我們可能就得將就那區區數千兆次的 FLOPS 了。