對SCHEME的一些理解(1)
最近抽空閱讀SICP,并重溫《黑客帝國》這個電影給了我在計算機程序設計上有了一些靈感的啟發,在正式談論SICP之前我想先對scheme及其在程序設計語言中的地位作個簡單解釋,LISP是一門非常古老的語言,據說僅FORTRAN比其老邁,在經歷多年落寞之后LISP突然煥發光彩,新近的流行語言不斷從中吸取養分,茁壯成長,比如C#加入LAMBDA表達式,JS的閉包,垃圾回收等,為什么LISP在分支方言眾多,而且沒有組織統一維護的不利狀況下能如此瀟灑得意。現在將自己的一些理解寫下筆記,方便后面溫習查詢。
我喜歡scheme,因為他小且純粹,我不指望能直接用LISP做一些現實開發,只希望靠它開啟我的思維,很多人之所以覺得LISP入門極難,或者SICP這本書起點太高,只是因為我們基礎太差,相信如果我們之前有離散數學、數理邏輯的基礎,看起來必定輕車熟路,因為相比計算機編程語言,LISP更像數學,它比C/C++更為基礎和原始,就如donald e.knuth所言,計算機科學只不過是數學的一層漂亮外衣,沒有離散數學、組合數學、概率論、抽象代數、基礎微積分等基礎數學知識,而直接閱讀《計算機程序設計藝術》,不但異常艱難,而且收效甚微,所以在看這套計算機科學經典之前,最好先看看作者的另一本書《具體數學》,這是題外話,現在我證明給你看為什么lisp比C更為原始,夸張一點說:更接近真理本質。
打個不太恰當的比喻吧,你覺得1米/秒和1千克哪個大?你覺得數字1和+運算符有什么關系?你可能會說,你不是白癡吧,兩個完全不同的東西,你怎能拿來類比,我想說的是,不要和"matrix"的奴隸一樣,要學"neo"盡可能看透這個世界,很多人都相信真理是簡單的,我也相信。幾百年前沒有人會覺得我們和植物有什么相同,而現在我們知道了,其實我們很像,都是由DNA/RNA傳遞遺傳信息,都由細胞構成;幾十年前沒有人覺得我們和水有什么相同,現在我們知道了,其實我們都是由有限的幾種元素組成。愛因斯坦發明了質能方程統一能量和質量,質量和能量其實可能只是一種事物的不同表現,不要被一切美麗的外表所迷惑,借用《黑客帝國》中的臺詞,何為真實?你所看到的?你所嗅到的?這些只不過是你大腦的反應。而現在的一切知識模型很可能在未來都會慢慢推翻,直到找到真理。
這里有一個可觀察和可測量的概念,就如牛頓的萬有引力,牛頓不知道萬有引力到底是什么,他只是找到了它的作用規律,在沒有找到真理之前,所有知識都只是事物的近似抽象模型,很多我們習以為常的概念卻不是那么顯而易見,比如:數字‘0’到底是什么,如何嚴格定義?你別告訴我一個圈就是個零,我也不清楚,但我知道這個問題不是那么顯而易見,比如如果一個“東西”它和任何其他東西的積是它本身,它就是零,那么只要一個東西滿足這個約束條件那他就是“0”,正好說明SICP第二章關于序對的闡釋,不管底下如何組織序對,我只需要一個可以生成序對、兩個取出前后元素的方法就行,SICP展示為了完全用過程表示數據的方式,讓人大吃一驚,我們關心的是可以真正觸摸產生效果的接口,一切無法觀察的東西對我們來說都是沒有意義的(見“看不見的龍”小故事),這個世界充滿著一個規律,世界上所有物體都是透過其可以對外界產生作用的接口讓外在得以察覺認識,我們知道的只是這些接口,而內部的真理卻無法窺見。
可以言歸正傳了,看看SICP習題2.6,整個SCHEME建立在唯一一條規則上:lambda演算。發明者是著名數理邏輯學家alonzo church,已經證明了lambda演算是圖靈完全的,所以一定程度上講基于lambda演算的scheme和基于機器模型的C語言計算能力是完全等同的。C語言中數據和操作是嚴格區分的,靜態的數據和動態的操作在我們腦海中已經根根深蒂固,但根據以上的論述,我可以想到,只要能發明一個東西,能完全滿足數字的定義,那這個東西就可以完全代替數字了(本質上就是),lambda演算還真有這個能力,用操作實現數據。以下一些概念可能牽涉到數學中的實分析(推薦一本書《陶哲軒實分析》,大家可以看看如何用基本規則構成復雜的數學大廈),如果沒有實分析理論基礎,看數據抽象這塊會有一些障礙。SICP中的例子非常簡單,要大家用lambda演算建立(整數)自然數體系及自然數加法規則。
它先定義了數字0:
(define zero (lambda (f) (lambda (x) x)))
然后定義了自增1操作:
(define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
很顯然有了這兩個規則,其他自然數都可以定義出來了:
1 = (add-1 zero)
2 = (add-1 (add-1 zero))
3 = (add-1 (add-1 (add-1 zero)))
...
這樣表示不夠直接,換成如下:
(define one (lambda (f) (lambda (x) (f x))))
(define two (lambda (f) (lambda (x) (f (f x)))))
(define three (lambda (f) (lambda (x) (f (f (f x))))))
大家看見了吧,任何自然數被都定義成過程,我們再在這些過程上定義等同的四則運算,然后提供轉換到阿拉伯自然數空間的方法,就建立了屬于自己的自然數體系了。這里SICP讓我們定義一個基于lambda自然數定義的原生加法,很簡單,a + b的加法效果只需要等同于0自增a加b次就算成功了,為了更簡單的檢查結果到底準確與否,先實現一個lambda自然數體系到阿拉伯自然數體系的簡單方法:
(define (print-x n)
(define (mark x)
(display "0"))
((n mark) 0))
使用方法如下:
(print-x three) => 000
會打印3個'0'字符,這樣我們就知道這個lambda數到底對應于自然數3了,這和原始人在還沒有數字概念的情況打繩結計數是一個道理:)
加法定義如下:
(define (lmd-add a b)
(lambda (f) (lambda (x)
((a f) ((b f) x)))))
測試下對不對:
(print-x (lmd-add two three)) => 00000
(print-x (add-1 (add-1 (add-1 (add-1 (add-1 zero)))))) => 00000
有興趣的朋友還可以在這個基礎上添加負數定義和加法運算。
scheme能用一條規則,推導出數字、四則運算符,甚至條件語句、循環語句等等,夠原始吧,原來在C語言中被我們看來是原子的數字、運算符、流程控制關鍵字都可以繼續分解為更細的粒度,例子中數字、加法,都只不過是lambda演算漂亮的語法糖衣(正如matrix 1中,最后neo看透了世界的外表,發現世界其實都是code)。
lisp不會衰落,只能會進化,因為他是數學家弄的一套數理邏輯,一個圖靈等價的lambda演算,更直白一點講就是用一種更簡潔的方式定義了圖靈機。更深層次上lisp代表了一種分析問題解決問題的思想,它和C語言所基于的數學原理從最本質上來講是完全一樣的,只是形式不同,scheme要強力依賴遞歸形式,其核心思想就是用簡單的規則遞歸組合出復雜規則,將復雜現象分析抽象成簡單規則的遞歸,這非常類似于人類大腦分析現實問題的模式(如果大家非常感興趣可以參看一本獲普利策大獎的科普書《GEB一條永恒金帶》),這與類C的命令式語言強調模塊治理的理念還是有很大差別的。我覺得lisp思想和分形很像,其大量用于復雜問題求解上,比如編譯、定理證明等,當然問題的層次化和模塊化與事物簡潔規則抽象上并不沖突,相反這兩種思想要在緊密配合才能將處理信息最小化,解決問題方式最優化。另外lisp給我一個明顯的提醒,沒有良好數學基礎的程序員走不了多遠(大家可以看看《編程本質》這本書,相信大家會理解這個觀點)。
《量子物理史話》小故事:
“我的車庫里有一條噴火的龍!”卡爾.薩根這樣聲稱。“太稀罕了!”他的朋友連忙跑到車庫中,但沒有看見龍。“龍在哪里?”“哦,”薩根說,“我忘了說明,這是一條隱身的龍。”朋友有些狐疑,不過他建議,可以撒一些粉末在地上,看看龍的爪印是不是會出現。但是薩根又聲稱,這龍是飄在空中的。 “那既然這條龍在噴火,我們用紅外線檢測儀做一個熱掃描?”“也不行。”薩根說,“隱形的火也沒有溫度。”“要么對這條龍噴漆讓它現形?”--“這條龍是非物質的,滑不溜手,油漆無處可粘。”反正沒有一種物理方法可以檢測到這條龍的存在。薩根最后問:“這樣一條看不見摸不著,沒有實體的,飄在空中噴著沒有熱度的火的龍,一條任何儀器都無法探測的龍,和‘根本沒有龍’之間又有什么差別呢?”。
浙公網安備 33010602011771號