日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

表面的简洁

發(fā)布時(shí)間:2025/5/22 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 表面的简洁 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

本文刊發(fā)在《程序員》雜志09年第二期上。是討論函數(shù)式語言基本性質(zhì)和發(fā)展方向的一篇文章。

?



一、把大象裝進(jìn)冰箱 =====在命令式語言(當(dāng)然我們可以確指為C、Delphi、Java或C#等等)中,初學(xué)者的第一 個(gè)疑難便是這樣的代碼(*注1):X=X+1為什么?因?yàn)樵跀?shù)學(xué)概念中,上述等式是不能成立的。這種表達(dá)式是計(jì)算機(jī)的思維邏 輯:當(dāng)它運(yùn)算上述表達(dá)式(或語句)時(shí),X被作為暫存單元——例如冰箱。為了讓冰箱產(chǎn) 生變化,比如解決“把大象裝進(jìn)冰箱”這樣的問題,我們需要如下三步:把冰箱門打開,把大象放進(jìn)去,把冰箱門關(guān)上(圖1:“把大象裝進(jìn)冰箱”的問題)因?yàn)槲覀冇袃芍皇謥矸謩e負(fù)責(zé)拉住冰箱門和大象,所以整個(gè)操作過程看起來很完美, 但接下來我們再加上點(diǎn)需求:我們要“把大象拿出來,把長頸鹿裝進(jìn)去”,怎么辦?是的, 應(yīng)該這樣:把冰箱門打開,把大象拿出來,把長頸鹿裝進(jìn)去,把冰箱門關(guān)上可惜長頸鹿有腿有思想,所以問題將會出在我們把大象拿出來的那一時(shí)刻:長頸鹿跑 掉了。為此,我們必須做很多的防護(hù)措施,例如先鎖住長頸鹿,再鎖住大象,以及在整個(gè) 過程中,保證冰箱門不會自動(dòng)關(guān)上或打開……而代碼:X=X+1 的執(zhí)行過程與此類似:當(dāng)CPU開始存取X這個(gè)位置時(shí),它只能在“當(dāng)前X”與“下一次X” 之間選擇二者之一。當(dāng)多個(gè)線程(或多個(gè)CPU)開始存取X這個(gè)位置時(shí),如果我們希望得 到相同的X值,那么我們就得在X操作過程中采取象上面一樣的防護(hù)措施:加一個(gè)鎖。以 保證要求所有線程都取完了這個(gè)“當(dāng)前X”值,才會被切換到“下一次X”值。由于這個(gè)限制,一旦多個(gè)線程都排著隊(duì)來看看這個(gè)X的美妙身形,整個(gè)隊(duì)列就全都慢 下來了。解決這個(gè)問題的辦法其實(shí)很簡單:只要X可見,我們就永遠(yuǎn)不修改這個(gè)X。而這,就 是函數(shù)式應(yīng)對大象問題的方法:如果冰箱里放著大象,就永遠(yuǎn)不要試圖放長頸鹿。所以在 函數(shù)式語言Erlang中的變量一旦賦值,就不能再修改。云風(fēng)曾在SD2C 2007大會上說:解決問題的最好方法,就是不解決它。這個(gè)觀點(diǎn)深得 函數(shù)式的精髓。二、帽子戲法的關(guān)鍵,在于至少多一頂帽子 =====雜技中的一種常見帽子戲法,是很多人圍成一圈或排成一排然后飛速地傳送手中的帽 子。這如果是一個(gè)人來表演,那么應(yīng)該是左右手各一頂帽子,而多出的一頂則總在頭上。   (圖2:帽子戲法)所以,關(guān)鍵在于至少要多出一頂帽子。正因?yàn)槎喑鰜磉@個(gè)帽子,所以我們看到雜技師 在我們面前構(gòu)建了一個(gè)往復(fù)不休的循環(huán)。事實(shí)上程序設(shè)計(jì)里的“循環(huán)”也存在完全相同的 問題:我們至少需要一個(gè)變量來保存迭代中的計(jì)數(shù),而且這個(gè)變量必須是可以修改的(*注 2)。然而這一要求既是玩轉(zhuǎn)“帽子戲法”的必要條件,卻又與我們上面講過的“不修改變 量”的原則相違背。函數(shù)式如何解決這個(gè)問題呢?其實(shí)答案還是相同的答案:至少多一頂帽子。只不過,帽子不一定要放在頭上,我們 可以把它放在傳遞的過程中——例如空中——就可以了。要知道,讓雜技師用同樣的方法 來擲蘋果,那多出來的一個(gè)就總是在空中了。在函數(shù)式中,我們?nèi)绻獦?gòu)建一個(gè)循環(huán),那么可以使用函數(shù)遞歸來實(shí)現(xiàn)。這上述控制 循環(huán)過程的變量,則可以把它放在函數(shù)形式參數(shù)表——這種類似“空中”的地方。與“空 中”相同的是,我們在靜態(tài)看函數(shù)時(shí),那是參數(shù)表;而運(yùn)行中時(shí),它傳入實(shí)參。然而帽子戲法的表演者并沒有三只手或更多只手,被循環(huán)帽子增加的時(shí)候,雜技師除 了加快速度之外,保證一個(gè)簡單的原則也是極其重要的:總是從帽子隊(duì)列的最末端取到下 一只帽子。這一原則保證了可以容納更多帽子,而又不會少處理任何一個(gè)。同樣的,遞歸 是消耗棧的,為了使棧空間不爆炸,解決的方法就是在最后一行代碼上調(diào)用遞歸,即尾遞 歸。因?yàn)槲策f歸的存在,函數(shù)最末的調(diào)用就可以被優(yōu)先為一行不消耗棧的跳轉(zhuǎn)指令,就像 帽子戲法的雜技師從帽子隊(duì)列上直接取走輪轉(zhuǎn)到手邊的帽子一樣。最后,對于一個(gè)函數(shù)來說,如果它只返回值而不修改函數(shù)外的任何東西,那么這個(gè)函 數(shù)就是安全的,它等價(jià)于它返回的這個(gè)值——如前所述的,這個(gè)值一旦有效(運(yùn)算出結(jié)果 并傳出),就不再變更。所以函數(shù)式中的函數(shù)調(diào)用,可以等價(jià)于一個(gè)表達(dá)式中運(yùn)算的值。如同函數(shù)調(diào)用,函數(shù)的遞歸也只返回值,所以也等價(jià)于一個(gè)表達(dá)式中運(yùn)算的值。再進(jìn) 一步的推論,遞歸實(shí)際上等效于循環(huán)求值。復(fù)雜的表象下,總會有一個(gè)簡單的原則。萬人的與一個(gè)人的帽子戲法,其原則是一樣 地:至少多一頂帽子,放在頭上,或是空中。三、計(jì)算機(jī)其實(shí)不認(rèn)得"hello" =====32位的unicode,以及128位的GUID等等,都直接與我們現(xiàn)在或?qū)淼拇鎯挝灰约?運(yùn)算的通道大小有關(guān)。事實(shí)上即使我們有128位機(jī)器,我們也只打算在這樣的通路上傳送 一個(gè)字符"h"——而不是字符串"hello"。從更為準(zhǔn)確的角度來說,事實(shí)上計(jì)算機(jī)也不認(rèn)得字 符"h",而只認(rèn)得數(shù)字0x68。同樣的,它也不認(rèn)得所謂的“真假(true/false)”,而只認(rèn)得數(shù)字 的1/0。(圖3:圖靈機(jī)的概念圖)我們編程的本質(zhì),其實(shí)不過是在求值一些數(shù)字而已。只是最終我們在自然語義上把這 些數(shù)字的一個(gè)連續(xù)或非連續(xù)的集合認(rèn)為是布爾值、字符串、數(shù)組或?qū)ο蟆.?dāng)我們認(rèn)識到運(yùn) 算求值的結(jié)果無非是數(shù)值,而表達(dá)形式又無非是連續(xù)或非連續(xù)時(shí),我們就得到了基本的數(shù) 據(jù)抽象單元:值、值系列。再加上我們前面講到的執(zhí)行體(函數(shù)),我們就得到了整個(gè)函數(shù) 式語言的鼻祖——LISP——的基本運(yùn)算模型:(+ Xn)其中,“( )”表明一個(gè)值系列(表),而“+”在這里指代某種運(yùn)算,Xn表明值(或值 系列)。整個(gè)的表返回一個(gè)值,因此也可以將“整個(gè)的表(通常這里稱為表達(dá)式)”等義為 一個(gè)值。任何的一個(gè)運(yùn)算,最終輸出的仍然只是一個(gè)或一系列數(shù)字,它被顯示在屏幕上, 便成了文本;放在內(nèi)存中,便成了數(shù)據(jù)。當(dāng)然,現(xiàn)實(shí)是這樣的機(jī)器最終從科學(xué)領(lǐng)域走向了民用,在PC(個(gè)人計(jì)算機(jī))普及的現(xiàn) 在,我們也需要讓類似LISP這樣的——絕對正確而又絕對非人性的——語言變得親切一 些。于是稍微復(fù)雜而有用一點(diǎn)的函數(shù)式語言,例如Erlang,通過豐富了上述的基本運(yùn)算模 型來使我們的視覺愉悅,或是在討論它的代碼時(shí)顯得神經(jīng)(略為)正常一些:(表一:以Erlang為例的、簡單的類型抽象)而我們逆推一份具體的Erlang代碼,其實(shí)仍然可以表達(dá)為上述的(+ Xn)。例如我們可 以在Erlang代碼在編譯階段使用解析樹(Abstract Form)中看到這樣的抽象代碼(abstract code): [{abstract_code,{raw_abstract_v1, [{attribute,1,file,{"./simplest.erl",1}}, {attribute,1,module,simplest}, {function,3,test,0,[{clause,3,[],[],[{atom,4|...}]}]},{eof,1}]}}] 無論是從形式,還是從實(shí)質(zhì)來看,這種解析樹(在erlang執(zhí)行中將會按照解析樹來生 成語法樹并執(zhí)行)與LISP語言的基本原則都是一致的。四、從“函數(shù)等義于值”到“函數(shù)是值” =====現(xiàn)在,JavaScript語言被更為深入的挖掘并漸漸了解到它的函數(shù)式語言本質(zhì),而類似 Erlang這樣的“天生傷害人的視力”的語言也移步前臺。這些語言的努力,使我們終于看 到一個(gè)屬于函數(shù)式語言的時(shí)代的曙光。在黎明之前的黑暗中,函數(shù)式以它諸多的、最不可 思議的特性迷惘著程序員的目光。連它最基本的概念說明,也如同玄學(xué)家的囈語:如同數(shù) 學(xué)函數(shù)是集合A(稱為定義域)中成員到集合B(稱為值域)中成員的映射,函數(shù)式語言 就是通過數(shù)學(xué)函數(shù)的定義、應(yīng)用的說明和求值完成運(yùn)算過程的。類似于這種等同于“什么也沒說”的解釋,其實(shí)的確是在闡述函數(shù)式語言的精髓。為 了減輕你的痛苦(但絕非輕視你的智商),我通常換個(gè)說法來陳述它們:如果表達(dá)式“1+1=2” 中的“+”被理解為求值函數(shù),那么所謂函數(shù)式語言,就是通過連續(xù)表達(dá)式運(yùn)算求值的語言; 既然上面的表達(dá)式可以算出結(jié)果“=2”,那么函數(shù)式語言自然也可以通過不停地求值找到問 題的答案。首先,在函數(shù)式語言中,函數(shù)只表明一個(gè)運(yùn)算過程,并產(chǎn)生一個(gè)運(yùn)算結(jié)果。這與表達(dá) 式中的運(yùn)算符具有完全相同的性質(zhì)——所以事實(shí)上一個(gè)函數(shù)式語言中,表達(dá)式的運(yùn)算符被 實(shí)現(xiàn)為一個(gè)函數(shù)。例如erlang的核心模塊中,可以導(dǎo)出類似這樣的函數(shù):(表二:Erlang中的運(yùn)算符其實(shí)對應(yīng)于內(nèi)核函數(shù)-部分舉例)所以,函數(shù)式語言的本質(zhì)是表達(dá)式/函數(shù)的“連續(xù)求值”。既然我們的輸出或存儲最終 只是在關(guān)心“值”,那么顯然連續(xù)求值的結(jié)果就可以直接作為他們的輸入。如果把輸出終端 或存儲看成接受輸入的設(shè)備,那么他也相當(dāng)于一個(gè)函數(shù);如果一臺計(jì)算設(shè)備只對外界表達(dá) 為一個(gè)或一系列輸出,并接受來自其他類似設(shè)備的輸入,那么計(jì)算設(shè)備本身也可以視為一 個(gè)函數(shù)……我們將這個(gè)過程放大,其實(shí)網(wǎng)絡(luò)可以是函數(shù)式的。這個(gè)就是著名的語用網(wǎng),它的理論 基礎(chǔ)是petri網(wǎng)論(Petri nets theory)。而事實(shí)上,作為計(jì)算模型來理解,它與函數(shù)式語言是 相似、等價(jià)的(*注3)。圖4:perti網(wǎng)的“庫所變換”函數(shù)式語言通過函數(shù)實(shí)現(xiàn)了三個(gè)基本的運(yùn)算邏輯(順序、分支與循環(huán)),因此它與我們 常用的命令式語言是等價(jià)的(*注4)。但是由于存在存儲問題,所以命令式語言是時(shí)序相關(guān) 的——即有存取某個(gè)存儲單元的先后問題。而函數(shù)式語言由于“把大象裝進(jìn)冰箱”之后就 再也不可更改,因此變得時(shí)序無關(guān)。以上述的petri網(wǎng)的例子來講,由于時(shí)序無關(guān),所以圖左側(cè)的“庫所(圓圈)”中的兩 個(gè)“消息(黑點(diǎn))”分解成右側(cè)的兩個(gè)庫所來處理時(shí),其轉(zhuǎn)換的代價(jià)為0(無鎖),而這個(gè) 過程應(yīng)用在多核機(jī)器或分布式網(wǎng)絡(luò)上時(shí),效率卻提高了一倍。更深層次地思考這個(gè)問題,由于在計(jì)算機(jī)系統(tǒng)中函數(shù)本身仍然是以數(shù)據(jù)形式存儲的, 所以函數(shù)事實(shí)上也是“被運(yùn)算的對象”和“運(yùn)算結(jié)果”。函數(shù)的這種特性被稱為高階函數(shù)。 “函數(shù)等義于值”是函數(shù)式的基礎(chǔ),而“函數(shù)是值”,則是高階函數(shù)的基礎(chǔ)。圖5:JavaScript統(tǒng)一語言范型的基本模型(*注5)當(dāng)“函數(shù)是值”時(shí),我們可以把一個(gè)函數(shù)傳遞到另一個(gè)地方去運(yùn)算,而其運(yùn)算結(jié)果仍 然是值,所以可以把一個(gè)等義的結(jié)果再傳回來。注意這一過程,就是分布運(yùn)算的實(shí)質(zhì),所 以,函數(shù)式在本質(zhì)上、天生地就是支持分布運(yùn)算的。無論我們是將“一段函數(shù)式代碼”所 表達(dá)的整個(gè)運(yùn)算過程分解成何種形式,并分布在何種復(fù)雜的運(yùn)算環(huán)境或網(wǎng)絡(luò)環(huán)境中,只要 最終在邏輯上它能得到一個(gè)值序列和一個(gè)運(yùn)算,就能夠成為更大范圍的分布網(wǎng)絡(luò)中的一個(gè) 結(jié)點(diǎn)。而這,就是整個(gè)計(jì)算世界的全部(注6):(symbol)。
==========注1:《Erlang程序設(shè)計(jì)》中,作者以這個(gè)例子為起始,來講述Erlang變量的單一賦值。注2:參見《結(jié)構(gòu)程序設(shè)計(jì)》,討論“如何刻畫計(jì)算的進(jìn)展”時(shí),作者E.W.Dijkstra說: (程序)如果含有循環(huán)語句,僅用語法指示器就不能描述計(jì)算的進(jìn)展了……(而應(yīng)該)引 進(jìn)一個(gè)“動(dòng)態(tài)指示器”毫不含糊地累計(jì)相應(yīng)的現(xiàn)行循環(huán)的序數(shù)……因?yàn)?#xff0c;語法指示器無法 充當(dāng)這種坐標(biāo)系統(tǒng)的一個(gè)組成成分。注3:"Implementing Coloured Petri Nets Using a Functional Programming Language" at http://portal.acm.org/citation.cfm?id=993039,and Functional Nets at http://lamp.epfl.ch/fn/注4:不同范型的計(jì)算機(jī)語言之間等價(jià)問題,可以歸結(jié)到圖靈等價(jià)這個(gè)命題上,這意 味著該運(yùn)算系統(tǒng)或模型能夠執(zhí)行任何復(fù)雜程度的、圖靈機(jī)可完成的數(shù)學(xué)運(yùn)算。2007年,Alex Smith證明了Wolfram提出了最小的“2,3圖靈機(jī)(兩種顏色,三種狀態(tài))”模型是最小完 備的圖靈等價(jià)系統(tǒng)。注5:這個(gè)統(tǒng)一過程用到了多項(xiàng)與函數(shù)式相關(guān)的基本設(shè)定:函數(shù)是執(zhí)行體、函數(shù)等義 于值、函數(shù)是值。注6:與“(+ Xn)”比較,這個(gè)表達(dá)式認(rèn)為“運(yùn)算 +”——也就是某個(gè)函數(shù)——其實(shí)也 是值,因此它也是“值系列”Xn中的一個(gè)部分。于是,當(dāng)由自然語義中的symbol來指代 Xn時(shí),整個(gè)表達(dá)式就變成了:(symbol)。建議閱讀作者的其它兩篇文章:《從表達(dá)式到變量:一行scheme代碼之所見》http://blog.csdn.net/aimingoo/archive/2007/02/12/1508118.aspx 《從表達(dá)式到函數(shù):表面的簡潔》http://blog.csdn.net/aimingoo/archive/2007/10/08/1815379.aspx

轉(zhuǎn)載于:https://www.cnblogs.com/encounter/archive/2009/04/22/2188605.html

總結(jié)

以上是生活随笔為你收集整理的表面的简洁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。