posts - 2, comments - 6, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          歡迎來到Lisp的世界

          本章的目標(biāo)是盡快讓你編程. 在本章結(jié)束的時(shí)候,你會(huì)掌握足夠的Common Lisp的知識(shí),可以開始寫程序了.

          范式(form)

          你可以通過使用Lisp而學(xué)習(xí)它,這是千真萬確的,因?yàn)長isp是交互式語言. 任何 Lisp系統(tǒng)都包含一個(gè)叫做頂層(toplevel)的交互式前端. 你在頂層中輸入Lisp表達(dá)式,系統(tǒng)打印它們的值. Lisp通常打印一個(gè)提示符表示它正在等待你的輸入. 許多Common Lisp的實(shí)現(xiàn)用$>$ 作為頂層提示符. 我們?cè)谶@兒也用此符號(hào). 最簡(jiǎn)單的Lisp表達(dá)式之一是一個(gè)整數(shù). 如果我們?cè)谔崾痉竺孑斎?,
          > 1
          1
          >
          
          系統(tǒng)會(huì)打印它的值,跟著另一個(gè)提示符,表示它在等待更多的輸入. 在這種情況下,打印出來的值和我們輸入的一樣. 象1這樣的數(shù)叫做自身求值的. 當(dāng)我們輸入一個(gè)需要做些求值工作的表達(dá)式時(shí),事情變得有趣起來. 例如,如果想把兩個(gè)數(shù)加起來,我們輸入:
          > (+ 2 3)
          5
          
          在表達(dá)式(+ 2 3)中,+叫做操作符,數(shù)23叫做變?cè)? 在日常生活中我們會(huì)把此表達(dá)式寫為2 + 3,但在Lisp中我們把+寫在最前面,后面跟著變?cè)?整個(gè)表達(dá)式被一對(duì)括號(hào)圍住:(+ 2 3). 因?yàn)椴僮鞣谇?這叫做前綴表示法. 一開始這樣寫表達(dá)式有點(diǎn)怪,但事實(shí)上這種表示法是 Lisp最好的東西之一. 比如,我們想把三個(gè)數(shù)加起來,用通常的表示法我們要寫+兩次:
          2 + 3 + 4
          
          而在Lisp中我們僅需增加一個(gè)變?cè)?
          > (+ 2 3 4)
          
          通常我們用+,它必須有兩個(gè)變?cè)?一個(gè)在左邊,一個(gè)在右邊. 前綴表示法的彈性意味著,在Lisp中,+可以接受任意數(shù)目的變?cè)?包括零個(gè):
          > (+)
          0
          > (+ 2)
          2
          > (+ 2 3)
          5
          > (+ 2 3 4)
          9
          > (+ 2 3 4 5)
          14
          
          因?yàn)椴僮鞣梢越邮懿煌瑪?shù)目的變?cè)?我們需要用括號(hào)指示表達(dá)式的開始和結(jié)束. 表達(dá)式可以嵌套. 即表達(dá)式中的變?cè)旧砜赡苁莻€(gè)復(fù)雜的表達(dá)式:
          > (/ (- 7 1) (- 4 2))
          3
          
          用自然語言來說,七減一的結(jié)果被四減二的結(jié)果除. 另一個(gè)Lisp表示法的漂亮之處是:它無所不包. 所有的Lisp表達(dá)式要么是象1這樣原子(atom),要么是放在括號(hào)中由零個(gè)或多個(gè)表達(dá)式組成的表(list). 這些是合法的Lisp表達(dá)式:
                  2   (+ 2 3)   (+ 2 3 4)   (/ (- 7 1) (- 4 2))
          
          正如我們將要看到的,所有的Lisp代碼都采取這種形式. 象C這樣的語言有著更復(fù)雜的語法:算術(shù)表達(dá)式用中綴表示法;函數(shù)調(diào)用類似前綴表示法,自變量用逗號(hào)隔開;表達(dá)式用分號(hào)隔開;而代碼塊用花括號(hào)分隔. 在Lisp中我們用單一的記號(hào)表達(dá)所有這些概念.

          求值

          在上一節(jié)中,我們?cè)陧攲永镙斎氡磉_(dá)式,Lisp顯示它們的值. 在這節(jié)里我們仔細(xì)觀察一下表達(dá)式是如何求值的. 在Lisp中,+是一個(gè)函數(shù),形如(+ 2 3)的表達(dá)式是函數(shù)調(diào)用. 當(dāng)Lisp對(duì)函數(shù)調(diào)用求值時(shí),它做這樣兩步:
          1. 首先變?cè)獜淖笾劣冶磺笾? 在此例中,每個(gè)變?cè)笾档阶陨?所以變?cè)闹捣謩e是2和3.
          2. 變?cè)闹祩鹘o以操作符命名的函數(shù). 在此例中,即+函數(shù),它返回5.
          如果任何變?cè)旧硎呛瘮?shù)調(diào)用,它們按上述規(guī)則求值. 這是對(duì)(/ (- 7 1) (- 4 2))求值時(shí)發(fā)生的情況:
          1. Lisp計(jì)算(- 7 1): 7求值為7,1求值為1. 它們被傳給函數(shù)-,它返回6.
          2. Lisp計(jì)算(- 4 2): 4求值為4,2求值為2. 它們被傳給函數(shù)-,它返回2.
          3. 6和2的值傳給函數(shù)/,它返回3.
          并不是所有的Lisp操作符都是函數(shù),但大多數(shù)都是. 而函數(shù)總是按這種方式求值的. 變?cè)獜淖笾劣冶磺笾?它們的值被傳給函數(shù),函數(shù)返回整個(gè)表達(dá)式的值. 這叫做Common Lisp的求值規(guī)則. 一個(gè)不遵守上述規(guī)則的操作符是quote. quote是一個(gè)特殊操作符,這意味著它有自己獨(dú)特的求值規(guī)則. 這個(gè)規(guī)則是:什么也不做. quote接受一個(gè)變?cè)?并且一字不差地返回它:
          > (quote (+ 3 5))
          (+ 3 5)
          
          為了方便,Common Lisp定義'作為quote的簡(jiǎn)記法. 通過在任何表達(dá)式前面加上' 你能獲得與調(diào)用quote同樣的效果:
          > '(+ 3 5)
          (+ 3 5)
          
          用簡(jiǎn)記法比用quote普遍得多. Lisp提供quote作為一種保護(hù)表達(dá)式以防被求值的手段. 下一節(jié)會(huì)解釋這種保護(hù)是多么有用.

           

          從麻煩中解脫出來 如果你輸入了一些Lisp不能理解的東西,它會(huì)打印一條出錯(cuò)信息并把你帶到一個(gè)叫中斷循環(huán)(break loop)的頂層中去. 中斷循環(huán)給了有經(jīng)驗(yàn)的程序員弄清出錯(cuò)原因的機(jī)會(huì), 不過一開始你唯一需要知道的事是如何從中斷循環(huán)中出來. 如何返回頂層取決于你用的Lisp環(huán)境. 在這個(gè)假設(shè)的環(huán)境里,用:abort出來:
          > (/ 1 0)
          Error: Division by zero.
          Options: :abort, :backtrace
          >> :abort
          >
          
          附錄A展示了如何調(diào)試Lisp程序,以及一些最常見錯(cuò)誤的例子.

          數(shù)據(jù)

          Lisp提供所有我們能在其它語言中找得到的數(shù)據(jù)類型,和一些我們找不到的. 一種我們?cè)缫咽褂玫臄?shù)據(jù)類型是整數(shù),它寫為一列數(shù)字:256. 另一種和其它語言一樣有的是字符串,它表示為一列用雙引號(hào)括起來的字符:"ora et labora". 整數(shù)和字符串都求值到自身. 另兩種我們不常在其它語言中發(fā)現(xiàn)的是符號(hào). 符號(hào)是單詞. 通常它們被轉(zhuǎn)換成大寫,不管你如何輸入:
          > 'Artichoke
          ARTICHOKE
          
          符號(hào)(通常)不求值為自身,因此如果你想引用一個(gè)符號(hào),請(qǐng)象上面那樣用'引用它. 表表示為被括號(hào)包圍的零個(gè)或多個(gè)元素. 元素可以是任何類型,包括表. 你必須引用表,否則Lisp會(huì)以為它是函數(shù)調(diào)用:
          > '(my 3 "Sons")
          (MY 3 "Sons")
          > '(the list (a b c) has 3 elements)
          (THE LIST (A B C) HAS 3 ELEMENTS)
          
          請(qǐng)注意一個(gè)引號(hào)保護(hù)整個(gè)表達(dá)式,包括里面的表達(dá)式. 你可以調(diào)用list來構(gòu)造表. 因?yàn)閘ist是一個(gè)函數(shù),它的變?cè)磺笾? 這是+調(diào)用在 list調(diào)用里的例子:
          > (list 'my (+ 2 1) "Sons")
          (MY 3 "Sons")
          
          現(xiàn)在我們處于欣賞Lisp最非同尋常特征之一的位置上. Lisp程序表達(dá)為表. 如果變?cè)臋C(jī)動(dòng)性和優(yōu)雅性沒能說服你Lisp記號(hào)是一種有價(jià)值的工具,這點(diǎn)應(yīng)該能使你信服. 這意味著Lisp程序可以生成Lisp代碼. Lisp程序員能(而且經(jīng)常)為自己編寫能寫程序的程序. 我們到第10章才考慮這種程序,但即使在現(xiàn)階段理解表達(dá)式和表的關(guān)系也是很重要的,而不是被它們弄糊涂. 這就是為何我們使用quote. 如果一個(gè)表被引用了, 求值返回它本身; 如果沒有被引用,它被認(rèn)為是代碼,求值返回它的值:
          > (list '(+ 2 1) (+ 2 1))
          ((+ 2 1) 3)
          
          此處第一個(gè)變?cè)灰昧?所以生成了一個(gè)表. 第二個(gè)變?cè)獩]有被引用,視之為函數(shù)調(diào)用,得出一個(gè)數(shù)字. 在Common Lisp中有兩種方法表示空表. 你可用一對(duì)不包含任何東西的括號(hào)來表示空表,或用符號(hào)nil來表示它. 你用哪種方法表示空表都沒有關(guān)系,不過它會(huì)被顯示成nil:
          > ()
          NIL
          > nil
          NIL
          
          你不必引用nil(雖然這也沒什么害處)因?yàn)閚il求值到自身.

          表的操作

          函數(shù)cons構(gòu)造表. 如果它的第二個(gè)變?cè)潜?它返回一個(gè)新表,新表的第一個(gè)元素就是第一個(gè)變?cè)?
          > (cons 'a '(b c d))
          (A B C D)
          
          我們可以通過把新元素cons到空表來構(gòu)造新表. 我們?cè)谏弦还?jié)見到的list函數(shù)只不過是一個(gè)把幾樣?xùn)|西cons到nil上去的方便辦法:
          > (cons 'a (cons 'b nil))
          (A B)
          > (list 'a 'b)
          (A B)
          
          基本的提取表中元素的函數(shù)是car和cdr.1 表的car就是它的第一個(gè)元素,而 cdr是第一個(gè)元素后面的所有東西:
          > (car '(a b c))
          A
          > (cdr '(a b c))
          (B C)
          
          你能用car和cdr的組合來取得表中任何元素. 如果你想取第三個(gè)元素,可以這樣:
          > (car (cdr (cdr '(a b c d))))
          C
          
          但是,你可以用third更容易地做同樣的事:
          > (third '(a b c d))
          C
          

          真值

          符號(hào)t是Common Lisp中表示真的缺省值. 就象nil,t求值到自身. 函數(shù)listp返回真如果它的變?cè)且粋€(gè)表:
          > (listp '(a b c))
          T
          
          一個(gè)函數(shù)叫做斷言如果它的返回值被解釋成真或假. Common Lisp的斷言的名字通常以p結(jié)尾. 假在Common Lisp中用nil(空表)來表示. 如果我們傳給listp的變?cè)皇潜?它返回nil:
          > (listp 27)
          NIL
          
          因?yàn)閚il扮演兩個(gè)角色,函數(shù)null返回真如果它的變?cè)强毡?
          > (null nil)
          T
          
          而函數(shù)not返回真如果它的變?cè)羌?
          > (not nil)
          T
          
          它們完全做的是同樣的事情. 要if是Common Lisp中最簡(jiǎn)單的條件語句. 它一般接受三個(gè)變?cè)?一個(gè)測(cè)試表達(dá)式, 一個(gè)then表達(dá)式和一個(gè)else表達(dá)式. 測(cè)試表達(dá)式被求值. 如果它返回真,則then 表達(dá)式被求值并返回結(jié)果. 如果它返回假,則else表達(dá)式被求值并返回它的結(jié)果:
          > (if (listp '(a b c))
          (+ 1 2)
          (+ 5 6))
          3
          > (if (listp 27)
          (+ 1 2)
          (+ 5 6))
          11
          
          就象quote,if是特殊操作符. 它不能用函數(shù)來實(shí)現(xiàn),因?yàn)楹瘮?shù)調(diào)用的變?cè)偸且笾档?而if的特點(diǎn)是只有最后兩個(gè)變?cè)械囊粋€(gè)被求值. if的最后一個(gè)變?cè)强蛇x的. 如果你省略它,它缺省為nil:
          > (if (listp 27)
          (+ 2 3))
          NIL
          
          雖然t是真的缺省表示,任何不是nil的東西在邏輯上下文中被認(rèn)為是真:
          > (if 27 1 2)
          1
          
          邏輯操作符and和or就象條件語句. 兩者都接受任意數(shù)目的變?cè)?但只求值能夠確定返回值的數(shù)目的變?cè)? 如果所有的變?cè)际钦?不是nil),那么and返回最后變?cè)闹?
          > (and t (+ 1 2))
          3
          
          但如果其中一個(gè)變?cè)羌?所有它后面的變?cè)疾磺笾盗? or也類似,只要它碰到一個(gè)是真的變?cè)屠^續(xù)求值了. 這兩個(gè)操作符是宏. 就象特殊操作符,宏可以規(guī)避通常的求值規(guī)則. 第10章解釋如何編寫你自己的宏.

          函數(shù)

          你可以用defun來定義新的函數(shù). 它通常接受三個(gè)以上變?cè)?一個(gè)名字,一列參數(shù), 和組成函數(shù)體的一個(gè)或多個(gè)表達(dá)式. 這是我們定義third的一種可能:
          > (defun our-third (x)
          (car (cdr (cdr x))))
          OUR-THIRD
          
          第一個(gè)變?cè)硎竞瘮?shù)名將是our-third. 第二個(gè)變?cè)?表(x),說明函數(shù)將接受一個(gè)變?cè)?x. 象這樣用作占位符的符號(hào)叫做變量. 當(dāng)變量代表傳給函數(shù)的變?cè)? 就象x所做的,它又叫做參數(shù). 定義的其余部分,(car (cdr (cdr x))),即通常所說的函數(shù)體. 它告訴Lisp,為了計(jì)算函數(shù)的返回值,它該做些什么. 所以,對(duì)我們給出的作為變?cè)娜魏蝬,調(diào)用 our-third會(huì)返回(car (cdr (cdr x))):
          > (our-third '(a b c d))
          C
          
          既然我們看到了變量,就更容易理解什么是符號(hào)了. 他們是變量名,是一種有自己名稱的對(duì)象. 這就是為什么符號(hào)要象表一樣必須被引用. 表必須被引用是因?yàn)椴蝗绱说脑?它就會(huì)被當(dāng)作代碼;符號(hào)必須被引用是因?yàn)椴蝗绱说脑?它就會(huì)被當(dāng)作變量. 你可以把函數(shù)定義想象成某個(gè)Lisp表達(dá)式的一般形式. 下面的表達(dá)式測(cè)試1和4之和是否大于3:
          > (> (+ 1 4) 3)
          T
          
          通過把這些特殊數(shù)字換成變量,我們可以寫一個(gè)函數(shù)測(cè)試任何兩個(gè)數(shù)之和是否大于第三個(gè):
          > (defun sum-greater (x y z)
          (> (+ x y) z))
          SUM-GREATER
          > (sum-greater 1 4 3)
          T
          
          Lisp對(duì)程序,過程或函數(shù)不加區(qū)別. 函數(shù)做了所有的事情(事實(shí)上構(gòu)成了語言本身的大部分). 你可以認(rèn)為你的函數(shù)中的一個(gè)是主函數(shù),但通常你能在頂層里調(diào)用任何一個(gè)函數(shù). 這意味著,當(dāng)你寫程序的時(shí)候,你能一小段一小段地測(cè)試它們.

          遞歸

          我們?cè)谏弦还?jié)中定義的函數(shù)還調(diào)用了其它函數(shù)為自己服務(wù). 比如sum-greater調(diào)用了+和>. 函數(shù)可以調(diào)用任何函數(shù),包括它本身. 自己調(diào)用自己的函數(shù)是遞歸的. Common Lisp函數(shù)member測(cè)試某樣?xùn)|西是否是一個(gè)表的元素. 這是定義成遞歸函數(shù)的簡(jiǎn)化版本:
          (defun our-member (obj lst)
          (if (null lst)
          nil
          (if (eql (car lst) obj)
          lst
          (our-member obj (cdr lst)))))
          
          斷言eql測(cè)試它的兩個(gè)變?cè)欠裣嗤?除此之外,定義中所有東西我們以前都見過. 這是它的運(yùn)行情況:
          > (our-member 'b '(a b c))
          (B C)
          > (our-member 'z '(a b c))
          NIL
          
          our-member的定義符合下面的自然語言描述. 為了測(cè)試一個(gè)對(duì)象obj是否是表lst 的成員,我們
          1. 首先檢查lst是否為空. 如果是空的,顯然obj不是它的成員,我們做完了.
          2. 否則如果obj是lst的第一個(gè)元素,它就是成員
          3. 否則只有當(dāng)obj是lst其余部分的成員時(shí),它才是lst的成員.
          當(dāng)你設(shè)法理解一個(gè)遞歸函數(shù)是如何工作的時(shí)候,把它翻譯成這樣的描述會(huì)有幫助的. 許多人一開始覺得遞歸函數(shù)很不好理解. 很多困難來自對(duì)函數(shù)的一個(gè)錯(cuò)誤的比喻. 有一種趨勢(shì)把函數(shù)想象成某種形式的機(jī)器. 原料就象參數(shù)一樣到來;一些工作被轉(zhuǎn)包給其它函數(shù);最后完成的產(chǎn)品被組裝好運(yùn)出去,就象返回一個(gè)值. 如果我們用這種比喻,遞歸就自相矛盾了. 機(jī)器怎么能把活兒轉(zhuǎn)包給自己? 它已經(jīng)忙著干活了. 一個(gè)更好的比喻是把函數(shù)想象成經(jīng)歷的過程. 在過程中遞歸是很自然的事情. 我們經(jīng)常在日常生活中看到遞歸過程. 比如,假設(shè)一個(gè)歷史學(xué)家對(duì)歐洲歷史上的人口變化感興趣. 檢查文獻(xiàn)的過程可能是這樣的:
          1. 取得一份文獻(xiàn)
          2. 查找有關(guān)人口變化的信息
          3. 如果該文獻(xiàn)提到其它可能有用的文獻(xiàn),檢查它們
          這個(gè)例子簡(jiǎn)單得足以理解,但它是遞歸的,因?yàn)榈谌娇梢园橛幸粋€(gè)或多個(gè)同樣的過程. 因此不要把our-member想象為測(cè)試某樣?xùn)|西是否在表中的機(jī)器,而是把它理解為決定某樣?xùn)|西是否在表中的規(guī)則. 如果我們以這種見解看待函數(shù),遞歸的悖論就不存在了.2

          閱讀Lisp

          上一節(jié)我們定義的our-member以五個(gè)括號(hào)結(jié)尾. 更加復(fù)雜的函數(shù)定義可能以七八個(gè)括號(hào)結(jié)尾. 初學(xué)Lisp的人看到這么多括號(hào)會(huì)感到氣餒. 這叫人如何去讀這樣的代碼? 更不用說寫了. 我怎么分辨括號(hào)之間的匹配? 回答是,你不需要做這些. Lisp程序員通過縮進(jìn),而不是括號(hào)來讀寫程序. 當(dāng)他們寫代碼的時(shí)候,他們讓文本編輯器顯示哪個(gè)括號(hào)匹配哪個(gè). 任何一個(gè)優(yōu)秀的編輯器,特別是Lisp系統(tǒng)附帶的,應(yīng)該能做到括號(hào)匹配. 在這樣的編輯器中,當(dāng)你輸入了一個(gè)括號(hào),它會(huì)指示和它匹配的那個(gè)括號(hào). 如果你的編輯器不做括號(hào)匹配,現(xiàn)在就停下來,研究一個(gè)如何使它做這件事,因?yàn)闆]有這個(gè)功能,事實(shí)上不可能寫Lisp 程序的. 在vi中,你可以用:set sm來打開括號(hào)匹配. 在emacs中,M-x lisp-mode是獲得該功能的好辦法. 有了好的編輯器,當(dāng)你寫程序的時(shí)候,括號(hào)匹配不再是個(gè)問題. 而且因?yàn)長isp縮進(jìn)有通用的慣例,你閱讀Lisp代碼也不是個(gè)問題了. 因?yàn)槿巳硕加孟嗤牧?xí)慣,你可以通過縮進(jìn)閱讀代碼,忽略括號(hào). 不管多么有經(jīng)驗(yàn)的Lisp黑客,會(huì)發(fā)現(xiàn)our-member的定義很難讀懂,如果它寫成這個(gè)樣子:
          (defun our-member (obj lst) (if (null lst) nil (if
          (eql (car lst) obj) lst (our-member obj (cdr lst)))))
          
          如果代碼適當(dāng)?shù)乜s進(jìn),他就沒有困難了. 你可以忽略大部分的括號(hào)而讀懂它:
          defun our-member (obj lst)
          if null lst
          nil
          if eql (car lst) obj
          lst
          our-member obj (cdr lst)
          
          事實(shí)上,當(dāng)你在紙上寫Lisp代碼的時(shí)候,這就是一個(gè)可行的辦法. 以后你輸入的時(shí)候,可以充分利用編輯器的匹配括號(hào)的功能.

          輸入和輸出

          到目前為止,我們一直在利用頂層暗中使用i/o. 對(duì)實(shí)際的交互式的程序,這可能還不夠. 在這一節(jié)中,我們看一些輸入輸出函數(shù). Common Lisp中最一般的輸出函數(shù)是format. 它接受兩個(gè)以上變?cè)?第一個(gè)表示輸出到哪兒,第二個(gè)是字符串模板,剩下的變?cè)ǔJ菍?duì)象,它們的打印表示 (printed representation)將被插入到模板中去. 這是個(gè)典型的例子:
          > (format t "~A plus ~A equals ~A.~%" 2 3 (+ 2 3))
          2 plus 3 equals 5.
          NIL
          
          注意兩樣?xùn)|西打印在這兒. 第一行是format打印的. 第二行是format調(diào)用的返回值,就象通常一樣由頂層打印. 通常象format這樣的函數(shù)不會(huì)直接在頂層,而是在程序內(nèi)部被調(diào)用,因此返回值就不會(huì)被看見. format的第一個(gè)變?cè)猼表示輸出將被送到缺省的地方去. 通常這會(huì)是頂層. 第二個(gè)變?cè)浅洚?dāng)輸出模板的字符串. 在它里面,每個(gè)  A*表示一個(gè)將被填充的位置, 而 %表示新行符. 這些位置依次被后面的變?cè)闹堤畛? 標(biāo)準(zhǔn)的輸入函數(shù)是read. 當(dāng)沒有變?cè)獣r(shí),它從缺省的地方--通常是頂層--讀入. 下面這個(gè)函數(shù)提示用戶輸入,然后返回任何輸入的東西:
          (defun askem (string)
          (format t "~A" string)
          (read))
          
          它運(yùn)行如下:
          > (askem "How old are you? ")
          How old are you? 29
          29
          
          請(qǐng)記住read會(huì)永遠(yuǎn)等在那兒直到你輸入什么東西并(通常要)敲入回車. 因此調(diào)用 read而不打印明確的提示信息是不明智的,否則你的程序會(huì)給人以已經(jīng)死掉的印象,但實(shí)際上它在等待輸入. 第二個(gè)要了解read的是它非常強(qiáng)大:它是一個(gè)完整的Lisp語法分析器. 它并不是讀入字符再把它們當(dāng)作字符串返回. 它分析所讀到的東西,并返回所產(chǎn)生的Lisp 對(duì)象. 在上例中, 它返回一個(gè)數(shù). askem雖然很短,但它展示了一些我們以前在函數(shù)定義中沒有看到的內(nèi)容. 它的函數(shù)體包含多個(gè)表達(dá)式. 函數(shù)體可以包含任意多個(gè)表達(dá)式,當(dāng)函數(shù)被調(diào)用時(shí),它們依次被求值,函數(shù)會(huì)返回最后一個(gè)表達(dá)式的值. 在以前的章節(jié)中,我們堅(jiān)持所謂的``純粹''的Lisp--即沒有副作用的Lisp. 副作用是指作為表達(dá)式求值的后果改變了外部世界的狀態(tài). 當(dāng)我們對(duì)一個(gè)純粹的Lisp 表達(dá)式,例如(+ 1 2)求值,沒有出現(xiàn)副作用;它僅返回一個(gè)值. 但當(dāng)我們調(diào)用 format,它不僅返回值,還打印了一些東西. 這是一種副作用. 如果我們要寫沒有副作用的代碼,那么定義有多個(gè)表達(dá)式的函數(shù)體就沒有什么意義. 最后一個(gè)表達(dá)式的值作為函數(shù)的返回值被返回了,但前面的表達(dá)式的值都被扔掉了. 如果這些表達(dá)式?jīng)]有副作用,你就不知道為什么Lisp要費(fèi)勁去計(jì)算它們.

          變量

          let是Common Lisp里最常用的操作符之一,它讓你引入新的局部變量:
          > (let ((x 1) (y 2))
          (+ x y))
          3
          
          一個(gè)let表達(dá)式有兩部分. 第一部分是一列創(chuàng)造新變量的指令,每個(gè)形如(變量 表達(dá)式). 每個(gè)變量會(huì)被賦予相應(yīng)的表達(dá)式的值. 在上例中,我們創(chuàng)造了兩個(gè)變量x 和y,它們分別被賦予初值1和2. 這些變量只在let的體內(nèi)有效. 變量和值的列表的后面是一組表達(dá)式,它們將被依次求值. 在此例中,只有一個(gè)表達(dá)式:對(duì)+的調(diào)用. 最后一個(gè)表達(dá)式的值作為let的值被返回. 下面是一個(gè)使用let 的更具選擇性的askem的版本:
          (defun ask-number ()
          (format t "Please enter a number. ")
          (let ((val (read)))
          (if (numberp val)
          val
          (ask-number))))
          
          此函數(shù)造了變量val來存放read返回的對(duì)象. 因?yàn)樗写藢?duì)象的名稱,它可以在作出是否要返回對(duì)象之前察看一下你的輸入值. 你可能已經(jīng)猜到,numberp是測(cè)試它的自變量是否是數(shù)字的斷言. 如果用戶輸入的不是數(shù)字,ask-number調(diào)用它自己. 結(jié)果產(chǎn)生了一個(gè)堅(jiān)持要得到一個(gè)數(shù)的函數(shù):
          > (ask-number)
          Please enter a number. a
          Please enter a number. (ho hum)
          Please enter a number. 52
          52
          
          象目前我們看到的變量都叫做局部變量. 它們只在特定的環(huán)境中是有效的. 另外有一類叫做全局變量的變量,它們?cè)谌魏蔚胤蕉际强梢姷?3 通過傳給defparameter一個(gè)符號(hào)和一個(gè)值,你可以構(gòu)造全局變量:
          > (defparameter *glob* 99)
          *GLOB*
          
          這樣的變量可以在任何地方存取,除非在一個(gè)表達(dá)式中,定義了一個(gè)相同名字的局部變量. 為了避免這種情況的出現(xiàn),習(xí)慣上全局變量的名字以星號(hào)開始和結(jié)束. 我們剛才定義的變量可讀作``星-glob-星''. 你還可以用defconstant定義全局常數(shù):
          (defconstant limit (+ *glob* 1))
          
          你不需要給常數(shù)起一個(gè)與眾不同的名字,因?yàn)槿绻褂孟嗤拿肿鳛樽兞?就會(huì)出錯(cuò). 如果你想知道某個(gè)符號(hào)是否是全局變量或常數(shù)的名字,請(qǐng)用boundp:
          > (boundp '*glob*)
          T
          

          賦值

          Common Lisp中最普通的賦值操作符是setf. 我們可以用它對(duì)全局或局部變量進(jìn)行賦值:
          > (setf *glob* 98)
          98
          > (let ((n 10))
          (setf n 2)
          n)
          2
          
          如果第一個(gè)自變量不是局部變量的名字,它被認(rèn)為是全局變量:
          > (setf x (list 'a 'b 'c))
          (A B C)
          
          即你可以通過賦值隱含地新建全局變量.不過在源文件中明確地使用 defparameter
          是較好的風(fēng)格. 你能做的遠(yuǎn)不止給變量賦值. setf的第一個(gè)自變量不但可以是變量名,還可以是表達(dá)式. 在這種情況下,第二個(gè)自變量的值被插入到第一個(gè)所涉及到的位置:
          > (setf (car x) 'n)
          N
          > x
          (N B C)
          
          setf的第一個(gè)自變量幾乎可以是任何涉及特定位置的表達(dá)式. 所有這樣的操作符在附錄D中都被標(biāo)記為``settable''. 你可以給setf偶數(shù)個(gè)自變量. 形如
          (setf a b
          c d
          e f)
          
          的表達(dá)式相當(dāng)于連續(xù)三個(gè)單獨(dú)的setf調(diào)用:
          (setf a b)
          (setf c d)
          (setf e f)
          

          函數(shù)化編程法

          函數(shù)化編程法的意思是編寫通過返回值來工作的程序,而不是修改什么東西. 它是 Lisp中占支配地位的范例. 大多數(shù)Lisp內(nèi)置函數(shù)被調(diào)用是為了得到它們的返回值, 而不是它們的副作用. 例如函數(shù)remove,它接受一個(gè)對(duì)象和一個(gè)表,返回一個(gè)排除了那個(gè)對(duì)象的新表:
          > (setf lst '(c a r a t))
          (C A R A T)
          > (remove 'a lst)
          (C R T)
          
          為什么不說remove從表中刪除一個(gè)對(duì)象? 因?yàn)檫@不是它所做的事情. 原來的表沒有被改變:
          > lst
          (C A R A T)
          
          那么如果你真想從表中刪掉一些元素怎么辦? 在Lisp中,你通常這樣做類似的事情:把表傳給某個(gè)函數(shù),然后用setf來處理返回值. 為了把所有的a從表x中刪掉, 我們這樣做:
          (setf x (remove 'a x))
          
          函數(shù)化編程法本質(zhì)上意味著避免使用諸如setf的函數(shù). 乍一看連想象這種可能性都很因難,別說試著去做了. 怎么能僅憑返回值就能構(gòu)造程序? 完全不利用副作用是有困難的. 但隨著學(xué)習(xí)的深入,你會(huì)驚訝地發(fā)現(xiàn)真正需要副作用的地方極少. 你使用副作用越少,你也就越進(jìn)步. 函數(shù)化編程最重要的優(yōu)點(diǎn)之一是它允許交互式測(cè)試. 在純粹的函數(shù)化代碼中,當(dāng)你寫函數(shù)的時(shí)候就可以測(cè)試它們. 如果它返回期望的值,你可以肯定它是正確的. 這些額外的信心,聚集在一起會(huì)產(chǎn)生巨大的影響. 當(dāng)你在程序中修改了任何地方, 你會(huì)得到即時(shí)的轉(zhuǎn)變. 而這種即時(shí)的轉(zhuǎn)變會(huì)帶來一種全新的編程風(fēng)格. 就象電話與信件相比,賦予我們新的通訊方式.

          迭代

          當(dāng)我們想做一些重復(fù)的事情時(shí),用迭代比用遞歸更自然些. 典型的例子是用迭代生成某種表格. 函數(shù)
          (defun show-squares (start end)
          (do ((i start (+ i 1)))
          ((> i end) 'done)
          (format t "~A ~A~%" i (* i i))))
          
          打印從start到end之間的整數(shù)的平方:
          > (show-squares 2 5)
          2 4
          3 9
          4 16
          5 25
          DONE
          
          do宏是Common Lisp中最基本的迭代操作符. 就象let,do也會(huì)產(chǎn)生變量,它的第一個(gè)自變量是關(guān)于變量規(guī)格的表. 表中的每個(gè)元素具有如下形式:
                  (variable initial update)
          
          其中variable是符號(hào),而initial和update是表達(dá)式. 一開始每個(gè)變量會(huì)被賦予相應(yīng)的initial的值;在迭代的時(shí)候它會(huì)被賦予相應(yīng)的update的值. show-squares中的do僅產(chǎn)生了一個(gè)變量i. 第一次迭代的時(shí)候,i被賦予start的值,在以后的迭代中它的值會(huì)每次增加1. do的第二個(gè)自變量是包含一個(gè)或多個(gè)表達(dá)式的表. 第一個(gè)表達(dá)式用來測(cè)試迭代是否應(yīng)該停止. 在上例中,測(cè)試是(> i end). 其余的表達(dá)式會(huì)在迭代停止后依次計(jì)算,并且最后一個(gè)的值作為do的返回值. 因此show-squares總是會(huì)返回done. 余下的自變量組成了循環(huán)體. 它們?cè)诿看蔚臅r(shí)候依次被求值. 在每次迭代的時(shí)候變量先被更新,然后終止測(cè)試被計(jì)算,再是(如果測(cè)試失敗)循環(huán)體被計(jì)算. 作為對(duì)比,這是遞歸版本的show-squares:
          (defun show-squares (i end)
          (if (> i end)
          'done
          (progn
          (format t "~A ~A~%" i (* i i))
          (show-squares (+ i 1) end))))
          
          此函數(shù)中的唯一新面孔是progn. 它接受任意數(shù)量的表達(dá)式,對(duì)它們依次求值,然后返回最后一個(gè)的值. 對(duì)一些特殊情況Common Lisp有更簡(jiǎn)單的迭代操作符. 比如,為了遍歷表的所有元素,你更可能用dolist. 這個(gè)函數(shù)返回表的長度:
          (defun our-length (lst)
          (let ((len 0))
          (dolist (obj lst)
          (setf len (+ len 1)))
          len))
          
          此處dolist接受形如(variable expression)的自變量,然后是表達(dá)式塊. variable相繼與expression返回的表中元素綁定,表達(dá)式塊被計(jì)算. 因此上面的循環(huán)在意思是,對(duì)lst中的每個(gè)obj,len增加1. 此函數(shù)的一個(gè)顯然的遞歸版本是:
          (defun our-length (lst)
          (if (null lst)
          0
          (+ (our-length (cdr lst)) 1)))
          
          即,如果表為空,它的長度就是0;否則它的長度是它的cdr的長度加上1. 此版本清楚一些,但因?yàn)樗皇俏策f歸的(見13.2節(jié)),它的效率不那么高.

          函數(shù)作為對(duì)象

          函數(shù)在Lisp中就象符號(hào),字符串和表一樣,是常規(guī)的對(duì)象. 如果我們給function一個(gè)函數(shù)的名字,它會(huì)返回相關(guān)的對(duì)象. 就象quote,function是特殊操作符,因此我們不必引用自變量:
          > (function +)
          #<Compiled-Function + 17BA4E>
          
          這個(gè)模樣很奇怪的返回值是函數(shù)在典型Common Lisp實(shí)現(xiàn)中可能的顯示方式. 到目前為止我們涉及到的對(duì)象具有這樣的特點(diǎn):Lisp顯示它們與我們輸入的模樣是一致的. 此慣例不適合函數(shù). 一個(gè)象+這樣的內(nèi)置函數(shù)在內(nèi)部可能是一段機(jī)器碼. Common Lisp的實(shí)現(xiàn)可以選擇任何它喜歡的外部表示方式. 就象我們用'作為quote的簡(jiǎn)記法,我們可以用#'作為function的簡(jiǎn)寫:
          > #'+
          #<Compiled-Function + 17BA4E>
          
          此簡(jiǎn)記法叫做sharp-quote. 就象其它的對(duì)象,函數(shù)可以作為自變量傳遞. 一個(gè)接受函數(shù)作為自變量的是 apply. 它接受一個(gè)函數(shù)和一列自變量,并返回那個(gè)函數(shù)應(yīng)用這些自變量后的結(jié)果:
          > (apply #'+ '(1 2 3))
          6
          > (+ 1 2 3)
          6
          
          它能接受任意數(shù)目的自變量,只要最后一個(gè)是表:
          > (apply #'+ 1 2 '(3 4 5))
          15
          
          函數(shù)funcall能做同樣的事情,不過它不需要把自變量放在表中:
          > (funcall #'+ 1 2 3)
          6
          
          宏defun創(chuàng)造一個(gè)函數(shù)并給它一個(gè)名字. 但函數(shù)并不是必須需要名字,因此我們也不需要用defun來定義它們. 就象其它Lisp對(duì)象一樣,我們可以直接引用函數(shù). 為了直接引用一個(gè)整數(shù),我們用一列數(shù)字;為了直接引用函數(shù),我們用所謂的 lambda表達(dá)式. 一個(gè)lambda表達(dá)式是包含以下元素的表:符號(hào)lambda,一列參數(shù), 然后是零個(gè)或多個(gè)表達(dá)式組成的函數(shù)體. 這個(gè)lambda表達(dá)式代表接受兩個(gè)數(shù)并返回它們之和的函數(shù):
          (lambda (x y)
          (+ x y))
          
          (x y)是參數(shù)表,跟在它后面的是函數(shù)體. 可以認(rèn)為lambda表達(dá)式是函數(shù)的名稱. 就象普通的函數(shù)名,lambda表達(dá)式可以是函數(shù)調(diào)用的第一個(gè)元素:
          > ((lambda (x) (+ x 100)) 1)
          101
          
          而通過在lamda表達(dá)式之前附加#',我們得到了相應(yīng)的函數(shù):
          > (funcall #'(lambda (x) (+ x 100))
          1)
          101
          
          此種表達(dá)法讓我們使用匿名函數(shù).

           

          lambda是什么? lambda表達(dá)式中的lambda不是操作符. 它僅是個(gè)符號(hào).4 它在早期的Lisp方言里有一種作用:函 數(shù)的內(nèi)部形態(tài)是表,因此區(qū)別函數(shù)和普通表的唯一辦法是查看第一個(gè)元素是否是 符號(hào)lambda. 在Common Lisp中你能把函數(shù)表示為表,但它們?cè)趦?nèi)部被表示成獨(dú)特的函數(shù)對(duì)象. 因此lambda不再是必需的. 如果要求把函數(shù)
          (lambda (x) (+ x 100))
          
          表示成
          ((x) (+ x 100))
          
          也沒有什么矛盾,但Lisp程序員已經(jīng)習(xí)慣了函數(shù)以符號(hào)lambda開始,因此Common Lisp保留了此傳統(tǒng).

          類型

          Lisp用非同尋常的靈活手段來處理類型. 在許多語言中,變量是有類型的,你得指定變量的類型才能使用它. 在Common Lisp中,值具有類型,而不是變量. 你可以假想每個(gè)對(duì)象都貼了指明它的類型的標(biāo)簽. 這種方法叫做顯示類型. 你不需要去聲明變量的類型,因?yàn)樽兞靠梢匝b任何類型的對(duì)象. 雖然類型聲明不是必需的,為了效率的緣故你可能會(huì)用到它們. 類型聲明在13.3 節(jié)中討論. Common Lisp的內(nèi)置類型構(gòu)成了一個(gè)類型的層次結(jié)構(gòu). 一個(gè)對(duì)象通常具有多種類型. 比如,數(shù)27是類型fixnum,integer,rational,real,number,atom,和t,以一般性的增長為序. (Numeric類型在第9章中討論)類型t是所有類型的超集,因此任何對(duì)象都是類型t. 函數(shù)typep接受一個(gè)對(duì)象和一個(gè)類型說明符,如果對(duì)象是那種類型就返回真:
          > (typep 27 'integer)
          T
          
          當(dāng)我們碰到各種內(nèi)置類型時(shí),我們會(huì)介紹它們.

          展望

          本章僅蜻蜓點(diǎn)水般地介紹了一下Lisp. 然而一種非同尋常的語言的形象已經(jīng)浮現(xiàn)出來了. 首先,該語言有單一的語法來表達(dá)所有的程序結(jié)構(gòu). 此語法基于一種Lisp 的對(duì)象--表. 函數(shù),作為獨(dú)特的Lisp對(duì)象,可以表示為表. 而且Lisp本身就是 Lisp程序,幾乎全是由與你自己定義的函數(shù)沒有任何區(qū)別的函數(shù)組成的. 如果你還不完全清楚所有這些概念之間的關(guān)系,請(qǐng)不必?fù)?dān)心. Lisp引入了這么多新穎的概念,你得花時(shí)間去熟悉它們. 不過至少得說明一件事: 令人吃驚的優(yōu)雅思想蘊(yùn)藏其中. Richard Gabriel曾半開玩笑地說C是適合寫Unix的語言.5 我們也可以說Lisp是編寫Lisp的語言. 但這是兩種不同的陳述. 一種可以用自己來編寫的語言是和一種擅長編寫某些特定類型的應(yīng)用的語言完全不同的. 它開啟了新的編程方法:你不但在語言中編程,你還可以改進(jìn)語言以適合你程序的需要. 如果你想理解Lisp編程的本質(zhì),這個(gè)思想是個(gè)很好的起點(diǎn).

           

          總結(jié)
          1. Lisp是交互式語言. 如果你在頂層輸入表達(dá)式,Lisp會(huì)打印它的值.
          2. Lisp程序由表達(dá)式組成. 表達(dá)式可以是一個(gè)原子,或是一個(gè)表, 表的第一個(gè)元素是操作符,后面跟著零個(gè)或多個(gè)自變量. 前綴表達(dá)式意味著操作符可接受任意多個(gè)自變量.
          3. Common Lisp函數(shù)調(diào)用的求值規(guī)則:從左至右對(duì)自變量求值,然后把這些值傳給由操作符表示的函數(shù). quote有它自己的求值規(guī)則:它原封不動(dòng)地返回自變量.
          4. 除了通常的數(shù)據(jù)類型,Lisp還有符號(hào)和表. 由于Lisp程序由表組成,很容易編寫能寫程序的程序.
          5. 三個(gè)基本的表處理函數(shù)是cons:它創(chuàng)造一個(gè)表;car:它返回表的頭一個(gè)元素; cdr:它返回第一個(gè)元素之后的所有東西.
          6. 在Common Lisp里, t表示真,nil表示偽. 在邏輯上下文中,除了nil之外的任何東西都算作真. 基本的條件語句是if. and和or操作符就象條件語句.
          7. Lisp主要是由函數(shù)構(gòu)成的. 你可用defun來定義新的函數(shù).
          8. 調(diào)用自己的函數(shù)是遞歸的. 遞歸函數(shù)應(yīng)該被認(rèn)為是一個(gè)過程而不是機(jī)器.
          9. 括號(hào)不是個(gè)問題,因?yàn)槌绦騿T利用縮進(jìn)來讀寫Lisp.
          10. 基本的i/o函數(shù)是read:它包含了完整的Lisp語法分析器,和format:它基于模板產(chǎn)生輸出.
          11. 你可以用let創(chuàng)造新的局部變量,用defparameter創(chuàng)造新的全局變量.
          12. 賦值操作符是setf. 它的第一個(gè)自變量可以是表達(dá)式.
          13. 函數(shù)化編程法--它意味著避免副作用--是Lisp中占支配地位的范例.
          14. 基本的循環(huán)操作符是do.
          15. 函數(shù)是常規(guī)的Lisp對(duì)象. 它們可以作為自變量被傳遞,可以表示成lambda 表達(dá)式.
          16. 值有類型,而變量沒有類型

           

          練習(xí)
          1. 解釋以下表達(dá)式求值后的結(jié)果:
            • a. (+ (- 5 1) (+ 3 7))
            • b. (list 1 (+ 2 3))
          2. 給出3種不同的能返回(a b c)的cons表達(dá)式
          3. 用car和cdr定義一個(gè)函數(shù),它返回表的第四個(gè)元素.
          4. 定義一個(gè)函數(shù),它接受兩個(gè)自變量,返回兩個(gè)中較大的一個(gè).
          5. 這些函數(shù)做了什么?
                a. (defun enigma (x)
                (and (not (null x))
                (or (null (car x))
                (enigma (cdr x)))))
                b. (defun mystery (x y)
                (if (null y)
                nil
                (if (eql (car y) x)
                0
                (let ((z (mystery x (cdr y))))
                (and z (+ z 1))))))
                
          6. 在下面的表達(dá)式中,x處應(yīng)該是什么可得出結(jié)果?
                a. > (car (x (cdr '(a (b c) d))))
                B
                b. > (x 13 (/ 1 0))
                13
                c. > (x #'list 1 nil)
                (1)
                
          7. 只用本章介紹的操作符,定義一個(gè)函數(shù),它接受一個(gè)表作為自變量,并返回t 如果表的元素中至少有一個(gè)類型是表.
          8. 給出函數(shù)的迭代和遞歸版本:它
                a. 接受一個(gè)正整數(shù),并打印這么多數(shù)目的點(diǎn).
                b. 接受一個(gè)表,返回符號(hào)a在表中出現(xiàn)的次數(shù).
                
          9. 一位朋友想寫一個(gè)函數(shù),它返回表中所有非nil元素之和. 他寫了此函數(shù)的兩個(gè)版本, 但沒有一個(gè)能正確工作. 請(qǐng)指出錯(cuò)誤在哪里,并給出正確的版本:
                a. (defun summit (lst)
                (remove nil lst)
                (apply #'+ lst))
                b. (defun summit (lst)
                (let ((x (car lst)))
                (if (null x)
                (summit (cdr lst))
                (+ x (summit (cdr lst))))))
                

          About this document ...

          This document was generated using the LaTeX2HTML translator Version 2K.1beta (1.48)

          Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
          Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

          The command line arguments were:
          latex2html -split=0 acl2.tex

          The translation was initiated by Dai Yuwen on 2003-07-29


          Footnotes

          ... 基本的提取表中元素的函數(shù)是car和cdr.1
          car和cdr的名字來源于表在第一個(gè)Lisp實(shí)現(xiàn)中的內(nèi)部表示. car表示``contents of the address part of the registe''而cdr表示``contents of the decrement part of the register.''
          ... 不存在了.2
          理解遞歸有困難的讀者可以參考以下文獻(xiàn)中的任何一種: Touretzky, David S. Common Lisp: A Gentle Introduction to Symbolic Computation. Benjamin/Cummings, Redwood City (CA), 1990, Chapter 8. Friedman, Daniel P., and Matthias Felleisen. The Little Lisper. MIT Press, Cambridge, 1987.
          ... 有一類叫做全局變量的變量,它們?cè)谌魏蔚胤蕉际强梢姷?3
          真正的區(qū)別在于詞法變量和特殊變量的不同,不過我們得到第六章才會(huì)考慮它.
          ... 它僅是個(gè)符號(hào).4
          在Ansi Common Lisp中還有一個(gè)lambda宏,它能讓你把#'(lambda (x) x)寫成(lambda (x) x). 由于使用這個(gè)宏模糊了lambda表達(dá)式和符號(hào)化的函數(shù)名(其中你得作用#') 的對(duì)稱性,它最多不過具有美觀的外表.
          ... Gabriel曾半開玩笑地說C是適合寫Unix的語言.5
          Gabriel, Richard P. Lisp: Good News, Bad News, How to Win Big. AI Expert, June 1991, p. 34.


          Dai Yuwen 2003-07-29

          評(píng)論

          # re: 歡迎來到Lisp的世界   回復(fù)  更多評(píng)論   

          2005-10-03 14:33 by 一條北極的魚
          http://blog.codelphi.com/solar04/category/2416.aspx

          haskell

          http://263.aka.org.cn/Docs/hacker-howto.html



          http://www.paulgraham.com/lisp.html


          http://www.gigamonkeys.com/book/


          http://spaces.msn.com/members/xlyyc/PersonalSpace.aspx?_c01_blogpart=blogmgmt&_c=blogpart


          http://ronaldmatt.blogchina.com/2808075.html

          # re: 歡迎來到Lisp的世界   回復(fù)  更多評(píng)論   

          2005-10-03 15:15 by 一條北極的魚
          http://140.112.2.84/~camd/


          http://micro.ee.nthu.edu.tw/~retry

          http://micro.ee.nthu.edu.tw/~maverick/ gimp.......................


          http://dsec.pku.edu.cn/~rli/WiKi/EmacsRegexp.html

          http://www-128.ibm.com/developerworks/cn/linux/l-scheme/part1/index.html



          http://www-128.ibm.com/developerworks/cn/linux/l-schm/part3/#resources



          http://mypage.iu.edu/~colallen/lp/ lllp

          http://mypage.iu.edu/~colallen/lp/node8.html


          http://cl-cookbook.sourceforge.net/

          http://www8.informatik.uni-erlangen.de/html/lisp-enter.html

          http://www.cs.cmu.edu/Groups/AI/html/faqs/lang/lisp/top.html


          http://www.ai.sri.com/~grasper/


          http://www.cs.cmu.edu/Groups/AI/html/cltl/cltl2.html


          http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/lang/lisp/0.html


          http://www.apl.jhu.edu/~hall/lisp.html

          # re: 歡迎來到Lisp的世界   回復(fù)  更多評(píng)論   

          2007-06-04 10:31 by dq
          您好譯者,是否可以把更多的章節(jié)放到網(wǎng)上,謝謝.

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 丘北县| 香格里拉县| 沽源县| 乌恰县| 晋宁县| 昔阳县| 姜堰市| 佛学| 博乐市| 阆中市| 盐池县| 罗江县| 永丰县| 安徽省| 柳林县| 江孜县| 阳江市| 淮北市| 黑山县| 宁都县| 柞水县| 长泰县| 旅游| 明水县| 清水县| 洛阳市| 临沧市| 车致| 房产| 侯马市| 汝南县| 宣汉县| 化州市| 惠州市| 苍梧县| 邵阳市| 什邡市| 绩溪县| 诸城市| 南宫市| 潞城市|