孤島引路人——為世間萬物取名字
上回介紹了兩種認識模式——「圖靈機」和「λ演算」。並且簡單入門了一下Lisp的現代版本Racket,讓大家簡單上手了一把。不過很多小夥伴即使沒有閱讀上一節也沒有關係,上一節只是一個引言,簡單介紹一下這一系列的文章的主要方向,沒有看也是沒有關係的。
這裡才是正片的開始。
也許會有人問,你講寫程序直接將JS,Python或者Java,我們想直接看到網頁,伺服器和資料庫,講這些做什麼用?
如果沒有完整了解程序本身就茫然進入應用級編程領域,我的親身經歷會告訴你,你會付出相當大的代價重新更正自己的思維。
當然,理論導向和興趣導向都是很好的學習方法,而且我之後也會推出相應的JavaScript,Python的系列文章,如果是需要興趣導向的小朋友可以直接看我以後的文章系列(如果有的話)。
至於閑來無事喜歡入深坑的小朋友,我們廢話不多說,開始咯!
人們既可以把這個世界叫做形體化了的音樂,也可以叫作形體化了的意志
----- 叔本華《作為表象的意志世界》
這個世界由基本粒子組成,基本物質的運動和其屬性有一定的規律,按照一定的規律,只要初始條件一定,其產生的運行結果也是一定的,上帝絕不是在擲骰子......
但是上帝真的不是在擲骰子么?
之所以我們會有這樣的想法,是因為我們有唯物主義價值觀,然而不管是什麼樣的價值觀,人都是厭惡失控的。一旦出現無法用公式,定理,或者教條,聖經無法解釋的東西,都會陷入到深深的不安之中。
所以在計算機程序這一人類創造的世界之中,我們傾注了對規則,可控世界的所有嚮往。
計算機程序實際上是對人思維活動的模擬
記得上節講到,人類的主要活動是「認識事物,改變事物」 。
其中認識事物, 便是將實際的物體抽象成思想中的對象(面向對象以後會提到),聯想一下我們在實際生活中是怎麼用語言確定具體事物的?
首先我們會約定一些基本元素,比如數字,顏色,幾何圖形,形容詞,相關語法等
然後我們會從這些基本的元素出發,形成新的元素,比如」黃色的圓「,」山的後面「最後我們會為一些複雜的元素起一些新的名字,比如「太陽就是每天早上從東邊山頭升起來的紅色的圓」,約定了這個,我們以後只要用「太陽」,就可以指代這些複雜的元素。
翻開任何語言的字典,實際上都是這些過程的複製。
計算機程序也是這樣,因此每一種計算機程序設計語言都提供了以下三種機制,來描述人類的認知:
- 基本元素
- 組合的方法
- 抽象的方法
基本元素,是程序的基本組成部分,就如同人類語言中的數字,顏色等,比如Lisp中的數字123...,字元串等。
組合的方法就是將這些基本元素組合構成更複雜元素,比如:
(cons 1 2); cons 單元
cons單元是一種比較複雜的數據結構,其頭部指向傳入的第一個值,尾部指向傳入的第二個值。
「;」即之後一行的內容為注釋,主要是為了方便程序員理解,並不會被運行。
抽象的方法就是為複雜或簡單的元素起一個新的名字:
(define x (cons 1 2))x; print x => (1 . 2)
之後只要直接使用x,就能對其進行操作,不需要重新進行此複雜元素的構造了。
注!:很多語言將這個名字稱為變數,意思是存儲起來,並且在之後需要的時候進行改變,然而,Lisp作為函數式編程語言,並不提倡對字面量(名字)進行更改。實際上聲明(取名字)是必要的,而賦值(改變名字的意義)並不是必要的。因此,為了方便大家理解,我並不會稱其為變數,只叫它名字。
Lisp構成
Lisp是由原子和列表組成的,所謂原子,就是名字,數字,字元串,比如:
x*y*this-is-test; 烤串命名法this_is_test; 下劃線命名法thisIsTest; 駝峰命名法
這裡還演示了名字的命名格式,主要是這三種。
而列表,是一組包含原子的序列,構成了一種過程(即是函數),印證了上一節,改變事物與認識事物之間可以相互轉化的特性:
(define name 123);操作符 參數1 參數2[define name2 234];racket中可以使用{},[]等表示列表
其第一項為操作符(函數名),第二項以後為參數,中間用空格隔開,整個列表用(),{},[]包裹。
某些列表本身也可以作為參數:
(define x (+ 1 2))x; 單獨一行原子會在控制台列印出結果 3
Lisp的一大特性是,基本元素並不是名字,因此可以被重新define,但是只要是被define了的值,都不能再被define。
(define (define x) (+ x x)); define列表就是define函數,列表項的第二個參數為函數參數,後一個列表是對參數進行的處理(define 1); 函數會被直接列印 2(define x 1)(define x 2); 重複定義錯誤 unsaved editor:3:8: module: duplicate definition for identifier at: x in: (define-values (x) 2)(define 1 1)(define 1 1); 字元串和數字不可以被定義; 不可定義錯誤 unsaved editor:2:9: define: not an identifier, identifier with default, or keyword for procedure argument at: 1 in: (define (quote 1) (quote 1))
因此,Lisp並沒有所謂的**關鍵字**,但是還是建議不要重複定義基本元素。
相信到了這裡,大家都知道上一節最後的等式是什麼意思了吧?
將Lisp作為計算器
現在可以將DrRacket作為一個計算器,嘗試一下寫下複雜的計算算式,看看能不能適應這種節奏吧。
實際上還有更好玩的,例如:
假設「+」是乘法,「*」是減法,「-」是除法,「/」是加法,那麼((3-1)*2)+6/9等於多少?
對於Lisp來說簡直不要太簡單:
#lang racket(define (plus x y) (* x y))(define (multi x y) (- x y))(define (minus x y) (/ x y))(define (div x y) (+ x y))(div (plus (multi (minus 3 1) 2) 6) 9);結果為15
注意看組合過程的樣式,如果是在DrRacket中編程,在寫完之後沒有左括弧前方和每個右括弧後方進行換行,系統會自動排版成這個樣子,程序的運行速度也是由最右側往最左側運行,這邊是前綴表達式的優點之一。
Lisp程序運行時,會將所有函數展開為define後面的列表,然後再進行計算,比如上面的式子會被展開替換成:
(+ (* (- (/ 3 1) 2) 6) 9)
所以問題來了,Lisp是先展開成這個樣子再運行呢?還是先運行,等運行到需要計算的地方再進行展開和替換呢?
這便是計算序,這個下一節再講吧,概念太多。>_<
推薦閱讀:
※年底了,讓我們殺一個產品經理祭天。
※從國產熱播劇了解大眾流行熱點
※關於遊戲開發末期項目組成員積極性下降的思考
※人工智慧會幹掉產品經理嗎?
※WWDC2016 Apple的新特性