數學和程式設計-王垠部落格

小北哥哥和北媽發表於2016-01-04

數學和程式設計

好些人來信問我,要成為一個好的程式設計師,數學基礎要達到什麼樣的程度?十八年前,當我成為大學計算機系新生的時候,也為同樣的問題所困擾。面對學數學,物理等學科的同學,我感到自卑。經常有人說那些專業的知識更加精華一些,難度更高一些,那些專業的人畢業之後如果做程式設計工作,水平其實比計算機系畢業的還要高。直到幾年前深入研究程式語言之後,對這個問題我才得到了答案和解脫。由於好多程式設計新手遇到同樣的困擾,所以我想在這裡把這個問題詳細的闡述一下。

數學並不是電腦科學的基礎

很多人都盲目的認為,電腦科學是數學的一個分支,數學是電腦科學的基礎,數學是更加博大精深的科學。這些人以為只要學會了數學,程式設計的事情全都不在話下,然而事實卻並非如此。

事實其實是這樣的:

  • 電腦科學其實根本不是數學,它只不過借用了非常少,非常基礎的數學,比高中數學還要容易一點。所謂“高等數學”,在電腦科學裡面基本用不上。
  • 計算機是比數學更加基礎的工具,就像紙和筆一樣。計算機可以用來解決數學的問題,也可以用來解決不是數學的問題,比如工程的問題,藝術的問題,經濟的問題,社會的問題等等。
  • 電腦科學是完全獨立的學科。學習了數學和物理,並不能代替對電腦科學的學習。你必須針對電腦科學進行學習,才有可能成為好的程式設計師。
  • 數學家所用的語言,比起常見的程式語言(比如C++,Java)來說,其實是非常落後而糟糕的設計。所謂“數學的美感”,其實大部分是夜郎自大。
  • 99%的數學家都寫不出像樣的程式碼。

數學是異常糟糕的語言

這並不是危言聳聽。如果你深入研究過程式語言的理論,就會發現其實數學家們使用的那些符號,只不過是一種非常糟糕的程式語言。數學的理論很多是有用的,然而數學家門用於描述這些理論所用的語言,卻是紛繁複雜,缺乏一致性,可組合性(composability),簡單性,可用性。這也就是為什麼大部分人看到數學就頭痛。這不是他們不夠聰明,而是數學語言的“設計”有問題。人們學習數學的時候,其實只有少部分時間在思考它的精髓,而大部分時間是在折騰它的語法。

舉一個非常簡單的例子。如果你說cos2θ表示(cos θ)2,那麼理所當然,cos-1θ就應該表示1/(cos θ)了?可它偏偏不是!別被數學老師們的教條和藉口欺騙啦,他們總是告訴你:“你應該記住這些!” 可是你想過嗎:“憑什麼?” cos2θ表示(cos θ)2,而cos-1θ,明明是一模一樣的形式,表示的卻是arccos θ。一個是求冪,一個是呼叫反函式,風馬不及,卻寫成一個樣子。這樣的語言設計混淆不堪,卻喜歡以“約定俗成”作為藉口。

如果你再多看一些數學書,就會發現這只是數學語言幾百年累積下來的糟粕的冰山一角。數學書裡盡是各種上標下標,帶括號的上標下標,x,y,z,a,b,c,f,g,h,各種扭來扭去的希臘字母,希伯來字母…… 斜體,黑體,花體,雙影體,……用不同的字型來表示不同的“型別”。很多符號的含義,在不同的子領域裡面都不一樣。有些人上一門數學課,到最後還沒明白那些符號是什麼意思。

直到今天,數學家們寫書仍然非常不嚴謹。他們常犯的一個錯誤是把x2這樣的東西叫做“函式”(function)。其實x2根本不是一個函式,它只是一個表示式。你必須同時指明“x是引數”,加上x2,才會成為一個函式。所以正確的函式寫法其實看起來像這樣:f(x) = x2。或者如果你不想給它一個名字,可以借用lambda calculus的寫法,寫成:λx.x2

可是數學家們灰常的喜歡“約定俗成”。他們定了一些不成文的規矩是這樣:凡是叫“x”的,都是函式的引數,凡是叫“y”的,都可能是一個函式…… 所以你寫x2就可以表示λx.x2,而不需要顯式的寫出“λx”。殊不知這些約定俗成,看起來貌似可以讓你少寫幾個字,卻造成了許許多多的混淆和麻煩。比如,你在Mathematica裡面可以對 x2+y 求關於x的導數,而且會得到 y'(x) + 2x 這樣蹊蹺的結果,因為它認為y可能是一個函式。更奇怪的是,如果你在後面多加一個a,也就是對x2+y+a求導,你會得到 2x!那麼 y'(x) 到哪裡去了?莫名其妙……

相對而言,程式語言就嚴謹很多,所有的程式語言都要求你必須指出函式的引數叫什麼名字。像x2這樣的東西,在程式語言裡面不是一個函式(function),而只是一個表示式(expression)。即使 JavaScript 這樣毛病眾多的語言都是這樣。比如,你必須寫:

function (x) { return x * x }

那個括號裡的(x),顯式的宣告瞭變數的名字,避免了可能出現的混淆。我不是第一個指出這些問題的人。其實現代邏輯學的鼻祖Gottlob Frege在一百多年以前就在他的論文“Function and Concept”裡批評了數學家們的這種做法。可是數學界的表達方式直到今天還是一樣的混亂。

很多人學習微積分都覺得困難,其實問題不在他們,而在於萊布尼茲(Leibniz)。萊布尼茲設計來描述微積分的語言(∫,dx, dy, ...),從現代語言設計的角度來看,其實非常之糟糕,可以說是一塌糊塗。我不能怪萊布尼茲,他畢竟是幾百年前的人了,他不知道我們現在知道的很多東西。然而古人的設計,現在還不考慮改進,反而當成教條灌輸給學生,那就是不思進取了。

數學的語言不像程式語言,它的歷史太久,沒有經過系統的,考慮周全的,統一的設計。各種數學符號的出現,往往是歷史上某個數學家有天在黑板上隨手畫出一些古怪的符號,說這代表什麼,那代表什麼,…… 然後就定下來了。很多數學家只關心自己那塊狹窄的子領域,為自己的理論隨便設計出一套符號,完全不管這些是否跟其它子領域的符號相沖突。這就是為什麼不同的數學子領域裡寫出同樣的符號,卻可以表示完全不同的涵義。在這種意義上,數學的語言跟Perl(一種非常糟糕的程式語言)有些類似。Perl把各種人需要的各種功能,不加選擇地加進了語言裡面,造成語言繁複不堪,甚至連Perl的創造者自己都不能理解它所有的功能。

數學的證明,使用的其實也是極其不嚴格的語言——古怪的符號,加上含糊不清,容易誤解的人類語言。如果你知道什麼是Curry-Howard Correspondence就會明白,其實每一個數學證明都不過是一段程式碼。同樣的定理,可以有許多不同版本的證明(程式碼)。這些證明有的簡短優雅,有的卻冗長繁複,像麵條一樣繞來繞去,沒法看懂。你經常在數學證明裡面看到“未定義的變數”,證明的邏輯也包含著各種隱含知識,思維跳躍,非常難以理解。很多數學證明,從程式的觀點來看,連編譯都不會通過,就別提執行了。

數學家們往往不在乎證明的優雅性。他們認為只要能證明出定理,你管我的證明簡不簡單,容不容易看懂呢。你越是看不懂,就越是覺得我高深莫測,越是感覺你自己笨!這種思潮到了程式設計的時候就顯出弊端了。數學家寫程式碼,往往忽視程式碼的優雅性,簡單性,模組化,可讀性,效能,資料結構等重要因素,認為程式碼只要能算出結果就行。他們把程式碼當成跟證明一樣,一次性的東西,所以他們的程式碼往往不能滿足實際工程的嚴格要求。

數學裡最在乎語言設計的分支,莫過於邏輯學了。很多人(包括很多程式語言專家)都盲目的崇拜邏輯學家,盲目的相信數理邏輯是優雅美好的語言。在程式語言界,數理邏輯已經成為一種災害,明明很容易就能解釋清楚的語義,非得寫成一堆稀奇古怪,含義混淆的邏輯公式。殊不知其實數理邏輯也是有很大的歷史遺留問題和誤區的。研究邏輯學的人經常遇到各種“不可判定”(undecidable)問題和所謂“悖論”(paradox),研究幾十年也沒搞清楚,而其實那些問題都是他們自己造出來的。你只需要把語言改一下,去掉一些不必要的功能,問題就沒了。但邏輯學家們總喜歡跟你說,那是某天才老祖宗想出來的,多麼多麼的了不起啊,不能改!

用一階邏輯(first-order logic)這樣的東西,你可以寫出一些毫無意義的語句。邏輯老師們會告訴你,記住啦,這些是沒有意義的,如果寫出來這些東西,是你的問題!他們沒有意識到,如果一個人可以用一個語言寫出毫無意義的東西,那麼這問題在於這個語言,而不在於這個人。一階邏輯號稱可以“表達所有數學”,結果事實卻是,沒有幾個數學家真的可以用它表達很有用的知識。到後來,稍微明智一點的邏輯學家們開始研究這些老古董語言到底出了什麼毛病,於是他們創造了Model Theory這樣的理論。寫出一些長篇大部頭,用於“驗證”這些邏輯語言的合理性。這些問題在我看來都是顯而易見的,因為很多邏輯的語言根本就不是很好很有用的東西。去研究它們“為什麼有毛病”,其實是白費力氣。自己另外設計一個更好語言就完事了。

在我看來,除了現代邏輯學的鼻祖Gottlob Frege理解了邏輯的精髓,其它邏輯學家基本都是照本宣科,一知半解。他們喜歡把簡單的問題搞複雜,製造一些新名詞,說得玄乎其玄靈丹妙藥似的。如果你想了解邏輯學的精華,建議你看看Frege的文集。看了之後你也許會發現,Frege思想的精華,其實已經融入在幾乎所有的程式語言裡了。

程式設計是一門藝術

從上面你也許已經明白了,普通程式設計師使用的程式語言,就算是C++這樣毛病眾多的語言,其實也已經比數學家使用的語言好很多。用數學的語言可以寫出含糊複雜的證明,在期刊或者學術會議上矇混過關,用程式語言寫出來的程式碼卻無法混過計算機這道嚴格的關卡。因為計算機不是人,它不會迷迷糊糊的點點頭讓你混過去,或者因為你是大師就不懂裝懂。程式碼是需要經過現實的檢驗的。如果你的程式碼有問題,它遲早會導致出問題。

電腦科學並不是數學的一個分支,它在很大程度上是優於數學,高於數學的。有些數學的基本理論可以被電腦科學所用,然而電腦科學並不是數學的一部分。數學在語言方面帶有太多的歷史遺留糟粕,它其實是泥菩薩過河,自身難保,它根本解決不了程式設計中遇到的實際問題。

程式設計真的是一門藝術,因為它符合藝術的各種特徵。藝術可以利用科學提供的工具,然而它卻不是科學的一部分,它的地位也並不低於科學。和所有的藝術一樣,程式設計能解決科學沒法解決的問題,滿足人們新的需求,開拓新的世界。所以親愛的程式設計師們,別再為自己不懂很多數學而煩惱了。數學並不能幫助你寫出好的程式,然而能寫出好程式的人,卻能更好的理解數學。我建議你們先學程式設計,再去看數學。

如果你想了解更多關於數學語言的弊病以及程式語言對它們的改進,我建議你看看這個Gerald Susman的講座

相關文章