程式語言是怎麼創造的(一 四則混合運算的實現)
一 實現四則混合運算
準備工作
實現一門語言往往看上去是高大上的,大部分開發人員都是在使用某一種語言。可能也沒去想過這個事兒,或者有這個想法也覺得是高不可攀然後不了了之了。但是其實這是一個認知上的錯誤,目前流行的語言(比如JavaScript,java,python)相當複雜(有各種歷史遺留的設計問題,以及為了向下相容的臃腫問題和一開始的設計思路問題)。如果一開始就試圖實現一個上述例子中的語言顯然是不可能的,這也不是我們認識語言的目的。
我一直試圖從人類的邏輯推理的最開始的角度來認識語言,我覺得語言應該是站在這個角度來認識,而不是站在機器的角度,因此,本文不需要讀者是否有計算機程式開發的經驗,當然如果有經驗的開發人員經過程式設計訓練在邏輯推理能力方面突出。有助於理解這篇文章,但這並非必要條件。
本文從最簡單的加減法開始,逐步實現四則混合運算,然後實現變數,函式。本文目標是80%的讀者都能看懂。
一 使用的工具:Racket
在這兒下載 ofollow,noindex">https://download.racket-lang.org/ 。
安裝後然後開啟

在開始選單找到它!
如果順利的話,你已經看到這個介面了。

racket的使用者介面,功能和按鈕很多,但一開始沒必要弄那麼複雜,很多功能也用不到,只看這三個地方就可以了。
二 一些基本的知識點(很簡單)。
計算的本質其實很簡單。

小朋友學算術就是從使用手指開始的,人類對計算的掌握也的確是從這幾個簡單的東西起步的。
關鍵知識點: 對計算的描述是通過表示式實現的,
表示式的定義:
1 數字 本身就是表示式;
2 操作符+資料 也是表示式;
3 表示式與操作符 也是表示式; (注意:這兒使用了遞迴定義)
S表示式(字首表達)作為一種符號邏輯來描述表示式
3 + 4 用S表示式表示出來 就是(+ 3 4) ,格式為: (操作符 資料 資料 資料 。。。)
(3+4)*(1+2)用S表示式就是 (* (+ 1 2) (+ 3 4)) ,格式為: (操作符 (操作符 資料 資料)(操作符 資料 資料 ))
雖然字首表示式看起來有點怪怪的,但是這種表示式的好處在於非常清晰的表達了操作符和運算元,並且對於(1+2+3+4)還可以減少符號 (+ 1 2 3 4),只要稍微適應一下就會體會到它的優點。

Racket 使用的是字首表示式。
對S表示式的操作(仍然非常簡單)

1 取得第一個元素的操作是 cdr ,取得剩餘部分的操作是 cdr。
【注意:】取得第剩下部分的第一個的操作是 cadr 等效於 car(cdr lst)
如果第一個本生就是一個列表,那麼取得第一個列表的第一個元素是 caar 等效於 car(car),以此類推 cdar,cddr,caadr。
2 定義一個變數的操作是define

car cdr define
3 在列表的前面增加一個元素 用cons ,兩個列表的合併 用apend

cons append
4 定義函式 也使用define

define
5 條件分支 if cond

if cond
6 變數與程式碼塊 let

let表示 定義的變數只能在這一個程式碼塊範圍能有效。
三 準備工作已經完成。
我們需要實現一個基本的程式語言需要的準備工作和知識點就是上面這幾個,的確是相當簡單的吧!坦率的說,一個沒有任何程式設計基礎的人也能容易的掌握上述的這些。而這些知識點也只是人類日常的邏輯中非常基本的組成。令人驚訝的是,這寫簡單的基本組成卻可以構成複雜到無法想象的人類智慧。接下來我們就要開始我們的語言創造的第一步,實現一個基本的四則混合運算,接下來的內容就要難一點兒了,將要用到線性遞迴和交叉遞迴。
四 實現四則混合運算
1 首先實現定義四個基本的兩個數的加減乘除

如果不出意外的話,你現在的電腦是也是這樣的效果!
這兒只定義了基本的兩個數的加減乘除。就這麼簡單的四個定義,就已經具備了任何四則混合運算的全部要素了。
多位數的加減乘除,例如(1 + 2 + 3 + 4) 等效於 (1+(2+(3+ (4+0))))。
因此我們可以根據現有的兩個數的加減乘除函式,根據上述思路,實現任意個數的加減乘除函式。
2 然後根據四個基本的加減乘除 實現不定引數的加減乘除
不定引數的加法函式實現
【注意】這兒開始難度就一下子增加很多了!,用到了 遞迴函式中的線性遞迴 ,如果沒有理解到的話,需要反覆多思考直到理解,遞迴是實現語言的基本條件。否則後面的就沒法看下去了。

有一個小技巧便於理解線性遞迴函式的定義:首先是當引數列表到最後完了的時候的求值(這時候一定不能遞迴呼叫本身,否則會進入無限迴圈);接下來的 else 後面就用 (cdr 引數表) 來代換上一步的求值,然後用(car 引數表) 去代換引數表的倒數第一個引數。
不定引數的減法函式的實現
這兒用到了上一步的加法函式,例如:(- 3 2 1) 會變換為: (- 3 (+ 2 1 0); (- 3 2) 變成 (- 3 (+ 2 0)) ;(-3) 變成 (- 3 (+ 0))。

減法器
不定引數個數的乘法函式和除法函式實現
也是先利用線性遞迴先實現乘法,然後再通過變換實現除法。

乘法器和除法器
定義一個輔助函式,根據符號就可以返回對應的函式

3 最後一步,對引數列表求值和對錶達式求值
【注意】這兒的難度就進一步增加了!,用到了 交差遞迴 ,如果沒有理解到的話,需要反覆多思考直到理解,交差遞迴也是是實現語言的基本條件。否則後面的就沒法看下去了。
我們來分析一個這樣的四則混合運算的例子:
(- 1 2 (* 5 6) (+ 7 4) 4 (/ 16 4 (- 5 2 1)) 6 (- 2 2 3))
引數表裡面除了第一個 1 和第二個 2 是數字,從第三個開始就變成另外一個計算表示式了。而且還可以在表示式裡面含有表示式,也就是說這個巢狀的層次也是不定的。因此需要遞迴的求值,直到全部都是數字。
下面是本章最難的部分,已經開始燒腦了,請仔細閱讀下圖中的註釋!!,最好在安靜的環境下思考和理解!!!。

上圖中的每一個註釋和箭頭說明都非常重要!!!
四 小結
恭喜你,已經看到這兒,順利的實現了一個四則混合運算了。如果上面的部分完全看懂並深刻理解了,那後面的部分就基本沒有什麼問題了。
其中 最難的部分 就是 線性遞迴和交叉遞迴!這個的確也沒有更簡單的辦法說清楚遞迴,只能依靠自己的理解和思考了。因為遞迴的本質就是在不同的維度跳躍,而迴圈只是在一個維度打轉,其理解起來的難道顯然不是一個數量級。
這一部分的程式碼請到此下載 https://gitee.com/zxbyh/scip/blob/master/R4/four-mixed-operations.rkt ,然後在自己電腦上執行。
接下來我們將進入第二階段: 變數的定義和使用。
#