深入底層:在編程中計(jì)算過程是如何求值的?
最近準(zhǔn)備啃一本書<<計(jì)算機(jī)程序的構(gòu)造與解釋>>(國內(nèi)簡(jiǎn)稱sicp)。僅僅看了第一章前面兩節(jié),對(duì)計(jì)算過程又有了深刻的理解。書中用的語言是lisp,當(dāng)我還在抱怨為什么不用當(dāng)時(shí)流行的c時(shí),讀了幾頁就領(lǐng)悟了作者的用意:?人們常說"c生萬物", 沒錯(cuò),不過那僅僅是在語言的實(shí)現(xiàn)層面上說的,但在計(jì)算過程中, 我敢肯定: lisp生萬物!
那么什么是計(jì)算過程呢? 在數(shù)學(xué)的角度,也許是一個(gè)證明的過程。但是在編程的角度,其實(shí)就是在研究一個(gè)程序求值的過程。
一個(gè)程序本質(zhì)上就是在求值,在求值過程中,本質(zhì)上就是將所有東西轉(zhuǎn)化為兩種最基本的東西--數(shù)據(jù)和運(yùn)算符(實(shí)際上編譯器的實(shí)現(xiàn)就是這個(gè)過程)。
舉個(gè)非常簡(jiǎn)單的例子:我們要編寫個(gè)函數(shù)計(jì)算x^2 + y^2, c語言可以這樣寫:
假如我們調(diào)用square_sum(3, 4)會(huì)發(fā)生什么? 作者在書中抽象出了代換模型這種概念: 將square_sum的參數(shù)x和y代換為3和4,接著向下求值, 最后就會(huì)變?yōu)?3 ^ 3 + 4 ^ 4了.
用lisp更能容易發(fā)現(xiàn)這一過程:
調(diào)用后lisp后轉(zhuǎn)化為:
非常簡(jiǎn)單吧,但是這里提出一個(gè)問題了:?
在調(diào)用函數(shù)時(shí)函數(shù)是先展開后求值(正則序)還是先求值后展開(應(yīng)用序)呢? 首先,數(shù)學(xué)角度證明了這兩種方法都能得出同樣的結(jié)果。
在lisp中, 我們先用過程抽象的方法自己定義一個(gè)if語句:
我們來測(cè)試一下lisp是正則序求值還是應(yīng)用序求值:
上面的結(jié)果是 badgood, 也就是說明 lisp 會(huì)先求出函數(shù)參數(shù)中的值,在應(yīng)用代換模型向下展開運(yùn)算了,那么試下正常的 if 語句:
那么就說明 if 不是用函數(shù)實(shí)現(xiàn)的吧(當(dāng)然了,if 的底層只是個(gè)指令,函數(shù)卻是一個(gè)堆棧結(jié)構(gòu))!
最后得出結(jié)論: lisp 求值過程是應(yīng)用序的!!!
心血來潮,測(cè)試一下 c語言是不是也是應(yīng)用序求值:
我們先分析一下,如果 c 語言是應(yīng)用序求值,那么在調(diào)用test的時(shí)候會(huì)先對(duì)參數(shù)求值, 注意到第二個(gè)參數(shù)是個(gè)死遞歸(沒有出口的遞歸), 那么這個(gè)程序會(huì)一直運(yùn)行沒有結(jié)果。
測(cè)試了一下,果然沒看到0輸出!!! 看來c語言也是應(yīng)用序求值! 最后再來看一下生成的匯編代碼:
總結(jié)一下吧,我們先討論了代換模型,最后又對(duì)求值順序進(jìn)行了探究。發(fā)現(xiàn)lisp和c都是應(yīng)用序的。
那么有沒有編程語言求值過程是正則序的呢? 我測(cè)試了幾種常用的語言暫時(shí)還沒發(fā)現(xiàn)。。?;蛟S使用應(yīng)用序隱藏著某種優(yōu)點(diǎn)?