談談數學與程式的「優雅」 — 抽象化
一口氣解決一系列問題的關鍵
對數學家來講,他們常常形容數學是優雅、美麗的,說研究數學帶來的美學體驗可與作畫、寫詩相比。但回想我在高中所學的數學,基本上都是以實際的計算為主,而幾乎沒有在大學數學系中會有的抽象化、邏輯推理。這想必也是大多數人對數學的印象,背公式與方法、然後套公式拿分數,幾乎沒有人會說數學是「優雅」的。但我在大四開始到數學系修課,並且直接閱讀目標讀者為數學系學生的數學課本之後,我終於可以肯定跟大家說 :「數學的確是優雅的 !」而我以前所認知的數學與真正的現代數學可以說是天差地遠。
會讓人說數學優雅的原因,我想其中一個原因就是它「抽象化」與「廣義化」的特性。與其引用維基百科的標準解釋,我在這裡嘗試用我自己的理解、用白話來解釋。什麼是抽象化 (abstraction) 呢 ?
舉例來說,生活中的狗有許多品種 : 拉不拉多、哈士奇、黃金獵犬、巴哥犬…等等數不清的種類。但當我們看到一條狗的時候,藉由某種所有狗都具有的共同特徵(四隻腳、會吠、吐舌頭),我們就可以判斷他是一隻狗,而這些特徵與一隻狗的確切品種或是年齡、性別都無關。所以說「狗」這個字,其實是比起「拉不拉多」更廣義、更抽象的一個概念。換句話說,狗這個字的應用範圍比較廣,對於細節的要求也比較少。在「狗」之上,我們也可以再更進一步抽象、來包括更多的動物進來 ,例如「哺乳類」這個概念。這個詞包括了所有的狗、貓、牛、人,我們並不在乎這些動物之間差異的部分,而只在乎他們共同的部分 ——透過哺乳來哺育下一代。抽象化就是抽取出狗、貓、牛、人等所有動物共同的特徵到「哺乳類」這個詞的過程。藉由適當的抽象化,我們可以抽取出事物背後的共同本質,更方便的同時談論多種事物,並得到可以廣泛被應用的結論。
現代的抽象數學便是建立在這樣的運作方式之上。以抽象代數中的基本物件 — 「群」為例。我們對群只有最基本、核心的假設。它是一個集合,其中的元素可以兩兩相作用而得到另一個元素,而且這樣的操作可以「逆」、符合結合律等等。藉由這樣抽象的假設我們可以同時談論很多數學物件:可以兩兩相加的整數、可以兩兩相乘的有理數。甚至是更複雜的物件 : 兩兩相乘的矩陣,兩兩結合的函數等等。在這個最核心的假設之上,數學家會透過證明來嚴謹的推導出關於群的「定理」。而這些定理可以套用於所有的群,也就是說我們同時得到了關於整數、有理數、矩陣、函數以及數不清數學物件的結論。藉由抽象的討論事物,再重新帶入例子,我們可能會發現原本具體的討論時都難以發現的現象。這正是我認為抽象數學優雅之處,可以說是一種優雅的懶惰,一次把所有類似的問題一併解決。
所以說數學世界也可以被想像成一個充滿各式動物的世界,而數學家們便是這個世界裡的生物學家,他們把數學物件抽象的分類,並且對於這些分類進行研究,來得出一個可以被廣泛應用的結論。
同樣的優雅也在寫程式的過程中出現,以物件導向的程式設計語言來說,都會提供類別的「繼承」(Inheritance) 功能,這正是這類語言能優雅地解決問題的關鍵之一。
如果我們今天要寫一個動物模擬器,那我們就會需要定義分別屬於狗、貓、山羊等動物的類別,但是事實上這些類別可能都有非常相似的功能 : 可以發出聲音、可以走路與跳躍、擁有一個位置與速度、可以被餵食。不優雅的解決辦法便是把專屬於狗的程式碼複製、貼上到貓、山羊的類別當中,再小作修改。但這樣不只麻煩、更會造成日後程式碼維護的困難。寫軟體的優雅之處便是 — 可以只做一次的事,我們就不應該傻傻再做一次。很多時候如果有造好的輪子可以用,我們就不應該花時間再造一次。在這裡我們應該就應該把這些同樣的功能抽象化並抽取到一個父類別中,再讓各種動物的類別從這裡繼承共同的功能,如此同樣的程式碼我們就只需要寫一次。例如我們就可以造一個「動物」的類別,來處理動物共有的功能 :
這個建立父類別的動作,其實就很類似數學中的廣義化 (Generalization) 的動作。一隻狗一定是一個動物,但動物有很多種未必是狗,所以動物為一個更加廣義且抽象的概念。如果我們今天想要模擬更多東西,也許是非生物的物件。那也許我們可以建立一個更廣義的「遊戲物件」類別,讓動物、交通工具、玩家等遊戲物件共同的功能都能一次被寫好 :
當然,類別與繼承只是其中一種抽象化的應用。我想只要是程式,就算只是一個函式,都有一定的抽象化及廣義的處理多種輸入的能力。一個函式的界面 (Interface),就好比一個數學證明所採用的假設。只要輸入符合界面的要求,就應該可以套用函式得到預期的輸出。這也好比只要一個數學物件符合定理要求的假設,就可以套用同樣的證明過程與結論。
寫程式的過程中很方便的一件事就是,如果需要處理圖片、處理文字,又或是套用演算法、訓練人工智慧,幾乎都已經有其他工程師所開發的函示庫可以直接使用。只要好好的與其介面接合,不同的程式區塊就能夠被優雅的拼裝起來。
在數學中,其實也有類似的事情。就像軟體工程師不會寫重複的程式一樣,好的數學家也不喜歡重複證明一樣的事物。群論、環論、體論、線性代數等等抽象數學的領域,也都可以被看作為一個函示庫,而一個好的數學定義就像提供一個好的介面。如果定義太抽象、廣義,那我們無法得出什麼有用的結論;反之定義的太具體,則應用範圍就被縮限。假如我們有了一個良好的「群」的定義,任何只要符和定義的數學物件都可以透過這樣的介面直接套用在群論中已被證明並導出的結論,很多時候新的發現就是在這樣的接合中發生。
在寫程式與學習數學的經驗中,我發現兩件事都能讓我感覺到相似的美感。在更近一步討論後,我發現兩件事在本質上竟也是如此的相似。他們都仰賴抽象化的力量來漂亮的解決問題,而這正是我覺得它們優雅之處。