欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Scheme 語(yǔ)言介紹

Wolfgang Kreutzer

翻譯:寒蟬退士

原文:http://www.cosc.canterbury.ac.nz/~wolfgang/cosc302/Chap2.3.html

譯者聲明:譯者對譯文不做任何擔保,譯者對譯文不擁有任何權利并且不負擔任何責任和義務(wù)。

APL 如同鉆石,有著(zhù)美妙的晶體結構;它的所有部分都以一致和優(yōu)美的方式關(guān)聯(lián)在一起。但是如果你嘗試以任何方式擴展這種結構 - 即使是增加另一個(gè)鉆石 - 你將得到一個(gè)丑陋的雜種。在另一方面,LISP 如同泥球。你可以向它增加任意數量的泥巴,它看起來(lái)還是個(gè)泥球。

[J. Moses, as quoted by Steele and Sussman (1978)].

譯序:本文是介紹人工智能的一本書(shū)的一章,Lisp 的赫赫聲名緣于它是人工智能專(zhuān)家們做符號處理的主要編程工具。從計算機技術(shù)的角度來(lái)說(shuō),Lisp 是函數式編程語(yǔ)言的代表,有著(zhù)“數學(xué)基礎”領(lǐng)域中 lambda 演算的理論背景。Lisp 的修正版本 Scheme 有著(zhù)一定的研究?jì)r(jià)值。


目錄:


歷史演化和關(guān)鍵概念

在人工智能的很多研究中,Lisp 家族語(yǔ)言是最古老的、并仍然是最廣泛使用的工具。不象 Fortran 那樣,在很大程度上出于經(jīng)濟上的動(dòng)機而保持語(yǔ)言存活了四分之一個(gè)世紀,Lisp 在 AI 社區的興旺是因為它的某些特征的優(yōu)越。

Lisp 至關(guān)重要的一個(gè)方面是試探性程序開(kāi)發(fā)的概念。符號到值的任何提交(commitment)可以延遲直到這樣的決定不可避免,即使如此它可以用很小的代價(jià)逆轉(reverse)。這允許我們快速的探索可供選擇的設計并逐步增加的建造程序。Lisp 的語(yǔ)法是簡(jiǎn)單的、并且它的程序自然的表示為數據結構,所以很容易寫(xiě)操縱其他程序的程序。

還有一些額外的因素被結合了進(jìn)來(lái),對 Lisp 的持久流行做出了貢獻[Hayes (1987)]: 不象 Fortan 或 Cobol,Lisp 持續自由的演化。直到最近 Lisp 程序的經(jīng)濟上的重要性仍是非常低的,這意味著(zhù)沒(méi)有任何壓力要凍結語(yǔ)言的定義為一個(gè)標準的特征集。作為一門(mén) AI 語(yǔ)言,Lisp 長(cháng)期用來(lái)攻擊“艱深”問(wèn)題,這種任務(wù)導致了很多新特征和強力工具。Lisp 社區總是接受一種工具建造文化,工具開(kāi)發(fā)者自身典型的也是工具的使用者的事實(shí)大力促成了這種事態(tài)。Lisp 是非常易于擴展的("增加更多泥巴") 并自愿“讓機器去做”。AI 研究總是準備擴展計算機的能力來(lái)更好的使用人類(lèi)資源,這個(gè)政策經(jīng)常導致工具超越了所在時(shí)代。被同樣的動(dòng)機所刺激,還有對編程環(huán)境和個(gè)人工作站的非常早期的興趣。

這種語(yǔ)言創(chuàng )新的精神已經(jīng)融入成 Lisp 歷史的一部分。這已經(jīng)由 McCarthy (1978) 和 Stoyan (1980) 詳細的寫(xiě)入編年史中。Lisp (List Processing Language) 最初設計為 Fortran 的一個(gè)擴展。John McCarthy 那時(shí)是 Dartmouth 學(xué)院的數學(xué)助理教授,他對使用計算機做符號計算(就是說(shuō),數學(xué)表達式的簡(jiǎn)化)發(fā)生了早期的興趣, 在“Dartmouth Summer School on Artificial Intelligence”期間受 Newell 和 Simon 對 IPL 介紹的進(jìn)一步刺激,這個(gè)研討班是 1956 年 McCarthy 和 Shannon 和 Minsky 一起組織的。盡管那時(shí)沒(méi)有任何參與者對如何最終完成人工智能有任何概念,數值計算完全不重要好象是明顯的,而操縱符號表達式的能力好象是關(guān)鍵性的。IPL 已經(jīng)包括了表處理和遞歸的想法,但它在風(fēng)味上仍是非?!暗图壍摹?。Fortan,剛剛被 IBM 表彰為第一個(gè)真正的“高級”編程語(yǔ)言,好象是在正確方向上的前進(jìn)了一步。不幸的是 Fortran 的設計受滿(mǎn)足高效數值計算需要的支配,而導致的僵化使它對于操縱 McCarthy 感興趣的各種應用需要的高度動(dòng)態(tài)的結構是一個(gè)非常薄弱的基礎。一個(gè)自包含的表處理語(yǔ)言好象是更好的解決方案。此時(shí) McCarthy 也是派到歐洲 Algol(Algorithmic Language)委員會(huì )的美洲代表團成員。在這里它提出了條件表達式的概念并依次受到其他成員想法的影響。McCarthy 現在正視 Lisp 為帶有 Algol式語(yǔ)法的一個(gè)編譯語(yǔ)言,但是它的第一個(gè)原型 Lisp I,有著(zhù)非常不同的風(fēng)味。

Lisp“稀少的”語(yǔ)法結構和解釋性本質(zhì)是非常偶然的發(fā)展出來(lái)的。我們對這些事件的討論遵循 Stoyan (1980) 給出的細帳。在 1958 年晚些時(shí)候 McCarthy 獲得了在 MIT 電子工程系的一個(gè)助理教授職務(wù)。與 Minsky 一起,接著(zhù)是一個(gè)數學(xué)助理教授,他建立了“MIT 人工智能計劃”,配備了 2 個(gè)程序員,1 個(gè)秘書(shū),1 個(gè)電傳打字機和 6 個(gè)學(xué)生(其中有 Kleinrock、 Bobrow 和 Slagl - [Stoyan (1980), 165])。 這個(gè)小組開(kāi)始從事寫(xiě) Lisp 編譯器的一個(gè)適度的嘗試,但是 Fortan 計劃報告的 30 “人年”使完全實(shí)現好象是一個(gè)不可完成的目標。最初用匯編語(yǔ)言手工編碼了一些簡(jiǎn)單的表處理函數和圓括號包圍的前綴表達式(就是 “(plus 2 2)” )用于測試。這種表示法后來(lái)被稱(chēng)為“Cambridge Polish”。紀念 Quine (一個(gè)(麻省)劍橋哲學(xué)家) 和 Lukasiewicz (表達式的“波蘭表示法”前綴形式的發(fā)明者)。盡管實(shí)驗得到了合理的進(jìn)展,而遞歸和垃圾收集好象是小組必須最終解決的最困難的障礙,仍然沒(méi)有語(yǔ)言的精確定義。在 1959 年 McCarthy (1960) 寫(xiě)了一篇論文來(lái)展示 Lisp 等價(jià)于圖靈機,能作為“可計算性”的可作為替代的理論。為此他需要一個(gè)一致的表示法,可以使用符號表達式來(lái)描述 Lisp 表達式和 Lisp 函數二者。這導致了“引用”和“求值”操作符,它們最初單獨的用作促成一個(gè)證明的理論工具。但是這篇論文帶來(lái)了大量未預期的結果。這個(gè)計劃的程序員之一,S. Russell 用匯編語(yǔ)言實(shí)現了“求值”,從而在編譯器計劃完全開(kāi)始之前,提供了測試手工編碼的函數的方式。為了以解釋性方式運行這樣的簡(jiǎn)單 Lisp 程序,所有表達式都必須是嵌套進(jìn)去,這樣程序自身成為應用于某些數據的一個(gè)表達式。幸運的是 MIT 計算實(shí)驗室也介入了 MAC (Multi Access Computing)計劃中;最早期的努力是使用電傳終端進(jìn)入分時(shí)系統中。Lisp 解釋器與交換式電傳終端一起使用(著(zhù)名的“讀,求值,打印”周期)逐漸變得非常流行。這個(gè)解釋器識別的記號后來(lái)被稱(chēng)為“S-語(yǔ)言”,而第一個(gè)工作的 Lisp 系統在 1959 年 Association for Computing Machinery [McCarthy (1959)]年度會(huì )議上提出了。Lisp 1 有大約 90 個(gè)預定義函數,第一個(gè)應用例子是做初等函數的微分的一個(gè)簡(jiǎn)單例程。這個(gè)解釋器馬上被進(jìn)一步精制和擴展為 Lisp 1.5 [140 個(gè)函數 - McCarthy (1962)],它擁有所有后來(lái)的 Lisp 系統的“祖先”的榮耀。Lisp 1.5 迅速成為對于在 Boston 區域內從事語(yǔ)言轉換和人工智能工作的人很有吸引的工具。當他們離開(kāi)并受雇于其他地方的時(shí)候,他們經(jīng)常帶上一個(gè)復本,因而導致了廣泛的分布。McCarthy 自己在 1962 年去了 Stanford 大學(xué)。因為他的研究轉移到更加理論性的領(lǐng)域,而沒(méi)有被牽涉到語(yǔ)言的進(jìn)一步開(kāi)發(fā)中。

Lisp 2,Algol 式的編譯版本從未出現。這有很多原因。首先,在解釋性上下文中 S 語(yǔ)言“稀少的”語(yǔ)法(Lisp = "Lots of Irritating, Spurious Parentheses")被證實(shí)是資產(chǎn)而不是債務(wù)。它使分析表達式非常的容易,因此允許快速開(kāi)發(fā)嫁接在 Lisp 之上的子語(yǔ)言。幾乎所有所謂的“AI 語(yǔ)言”的都以這種方式實(shí)現的,很多 Lisp 程序員猛烈的抵制介入語(yǔ)法糖衣的額外層。Lisp 2 死亡的另一個(gè)因素是歸咎于“creeping featuritis”[Clinger (1988a), 25]。60 年代早期在 BBN(Bolt, Beranek 和 Newman)成立了一個(gè)委員會(huì )定義 Lisp 2 的特征。這導致語(yǔ)言規定迅速增長(cháng),為了保持“每個(gè)人”都高興而增加新特征。實(shí)現由于財務(wù)上的限制最終被廢棄了并從未復活過(guò)。在 Edinburgh 大學(xué)開(kāi)發(fā)的 Pop [Burstall 等 (1971), Burton 和 Shadbolt (1987)],可能最接近于 Lisp 2 的樣子?,F代 Lisp 系統的語(yǔ)法同最初的“S-表示法”是一致的。McCarthy 自己總是稱(chēng)之為“M-表示法”[McCarthy et al. (1962)],它也用于 Allen (1978)優(yōu)秀的教科書(shū)中。Allen 稱(chēng)“M-表示法”為規定而“S-表示法”是表示語(yǔ)言。

很多基于 Lisp 1.5 的主要方言出現了。所有這些都基于一個(gè)共同祖先,但在提供的特征和工具上顯示了引人注意的區別。這些不同在傳統上通過(guò)叫做“兼容函數”的包來(lái)跨越,它在另一個(gè)上下文中解釋一個(gè)語(yǔ)言的構造。當然,這種方式在計算上是有花費的,而對于支持嚴肅工作通常需要某種形式的“手工”轉換。

定理證明(比如 SAINT 和 SIN)和代數公式操縱系統(比如 MACSYMA)方面的工作,在 MIT 的 MAC 計劃導致了 MacLisp[Moon (1974)]。這個(gè)方言特別注意了數值計算的效率,這是對很多早期 Lisp 系統的常見(jiàn)的批評。MacLisp 自豪于一組豐富的數據類(lèi)型(數組,hunk ...),用于程序開(kāi)發(fā)和調試的用戶(hù)友好的環(huán)境,和一組豐富的預定義函數(大約 300 個(gè))。它還擔當了 ZetaLisp 的基礎,這是用于 MIT (現在是 Symbolics)的 Lisp-機器計劃[Weinreb 和 Moon (1981)]的 Lisp 版本?!皹藴省?Lisp 和它的后代 “可移植”標準 Lisp [Griss 和 Morrison (1981)] 由 Hearn 和其他人在 Utah 大學(xué)設計和實(shí)現。 最初用作 REDUCE,和其他公式操縱系統的基礎,它現在是 Hewlett Packard 工作站的 Lisp 方言,它的名字有某種誤導,因為它明確的不是 Lisp 標準,并且也不比其他方言容易移植。

在 J. McCarthy 離開(kāi) MIT 到 Stanford 的時(shí)候,很多他以前的研究生被其他大學(xué)和研究機構雇傭。Bobrow 兩兄弟工作于 Bolt, Beranek and Newman,一個(gè)美國研究公司。D. Bobrow 加入了在 California 的 Xerox Palo Alto 研究中心(PARC)。一段時(shí)間內在這兩個(gè)機構都維持著(zhù)一個(gè)聯(lián)合開(kāi)發(fā)的 Lisp 系統(Interlisp - Teitelman (1974))。Interlisp 也用在 Stanford 大學(xué),在這里它成長(cháng)為今天能獲得的最復雜的 Lisp 系統(大約 600 個(gè)預定義函數),帶有大量的工具和被認為最用戶(hù)友好的環(huán)境[Sandewall (1978), Teitelman 和 Masinter (1981)]。這個(gè)系統的一個(gè)更新版本(Interlisp-D)后來(lái)在 Xerox “Lisp 機器”上(就是 Dandelion, Dorado 和 Dolphin),還被移植到 Siemens 和 IBM 設備上。

Bobrow 兩兄弟的第二個(gè),R. Bobrow,最終擔任 California 大學(xué)在 Irvine (UCI)的教師,在這里他開(kāi)發(fā)了 UCI-Lisp [Meehan (1979)],這是在很多研究機構流行的方言。在很多年中 UCI-Lisp 充當 Carnegie Mellon 大學(xué)的 AI 程序的主要編程工具。

自從捐贈了第一個(gè) PDP6 到 MIT 的計算實(shí)驗室,DEC10 行列的機器在整個(gè)世界成為 AI 研究的標準設備; 同樣的,Lisp 被控制為一個(gè)編程工具。這種現象可以部分的由它被設計為一個(gè)真正分時(shí)機器的事實(shí)來(lái)解釋?zhuān)敃r(shí)多數其他廠(chǎng)商的設備仍是面向批處理的??焖偃?機交互被強制為支持 AI 的典型編程風(fēng)格。另一個(gè)起作用的因素在于主要的 AI 中心(MIT、Stanford、Carnegie Mellon、Edinburgh)使用這些機器用于他們大批的工作的事實(shí);使得要運行他們的軟件的拷貝就必須是選用 Lisp,而它又是自由發(fā)布的。在七十年代后期和八十年代的 Unix 操作系統的流行日益增長(cháng),仍然在 DEC 的小型機行列上產(chǎn)生了大量的 Lisp 實(shí)現。 PDP11 Lisp,再次基于 MacLisp,在 Harvard 開(kāi)發(fā)出來(lái)。當它的一些職員去 California 大學(xué) Berkeley 分校的時(shí)候被拿到了美國西海岸,這里已經(jīng)對 Unix 有了強烈的興趣。FranzLisp [Foderaro (1979)]是最終出現的方言。它是基于 Unix 的并運行在 VAX 和其他在大學(xué)環(huán)境流行的機器上(比如 Suns, ...)。它的持續流行很大程度歸功于它的低大學(xué)使用價(jià)格。

最近在標準化上的興趣復興了,很大程度上受使用 Lisp 作為專(zhuān)家系統開(kāi)發(fā)工具的刺激。很多主要計算機廠(chǎng)商委托設計叫做 Common Lisp 的“標準” Lisp 變體[Steele (1984)]?!白畛醯哪繕耸且幎ㄗ銐蚪咏?MacLisp 后代家族的每個(gè)成員的 Lisp,保證這些實(shí)現樂(lè )于朝向這個(gè)公共定義來(lái)發(fā)展自身。... 委員會(huì )的一個(gè)主要的子目標是用這個(gè) COMMON LISP 寫(xiě)的程序可以實(shí)現直接的源碼-可傳送,而不用管下層硬件” [Brooks 和 Gabriel (1984), p. 1]。迄今為止這個(gè)計劃好象成功了?,F在已經(jīng)存在了很多實(shí)現。但是,仍有待觀(guān)察對規整 Lisp 以前不受限制的“特征”增加的嘗試是否最終會(huì )成功[Allen (1987)]。仍有懷疑的余地,因為多數 Lisp 程序員是高度個(gè)人主義的,在風(fēng)格問(wèn)題上帶有強烈的信念,因為 Lisp 使實(shí)現“新”特征非常容易。已經(jīng)有對 Common Lisp 的大小和它的更加不可思議特征(經(jīng)常包括它們來(lái)確保與過(guò)去兼容)的普遍批評。但是,這是一個(gè)好機會(huì ),商業(yè)壓力將至少導致一個(gè)公共基礎,更加專(zhuān)門(mén)的方言或子集可以萌發(fā)于此,還有一個(gè)希望,反映在朝向 ISO Lisp 標準的努力上。

新出現的方言之一是 Scheme,一個(gè)詞法作用域版本的 MacLisp(象 NIL, T, ..)。Scheme 在 MIT 用來(lái)講授計算機編程的介紹性課程,并在 Abelson 等人(1985)的一本優(yōu)秀的書(shū)中被加以良好的文檔。從作者的觀(guān)點(diǎn)看來(lái) Scheme 是一個(gè)非常有表現力的和優(yōu)雅的語(yǔ)言,圍繞著(zhù)一個(gè)簡(jiǎn)單的小核心,準備了正交性和非常靈活的特征。嵌入到適當的交互編程環(huán)境中,它在實(shí)踐上反擊了通常針對  Lisp 的所有批評。它唯一的缺點(diǎn)在于缺乏與傳統的 Lisp 系統特別是 Common Lisp 的兼容性。

并行處理近些年已經(jīng)成為主要的研究焦點(diǎn),由此產(chǎn)生了很多語(yǔ)言提議。MultiLisp [Halstead (1985)] 是一個(gè)有趣的基于 Scheme 的例子。

用 Scheme 編程

Posted-Date: Mon, 6 Jul 87 07:46:39 EDT
Date: Mon, 6 Jul 87 07:46:39 EDT
From: Tigger@Hundred-Acre-Woods.Milne.Disney
To: ramsdell@linus.uucp
Subject: Scheme

關(guān)于 Scheme 的美妙的事情是:
Scheme 是一個(gè)漂亮的東西。
復雜的過(guò)程式的想法
可以通過(guò)簡(jiǎn)單字符串來(lái)表達。
它清晰的語(yǔ)義,和缺乏書(shū)生氣,
有助于使程序運行,運行,再運行!
然而關(guān)于 Scheme 的最美妙的事情是:
用它編程很好玩,
用它編程太好玩了! [Ramsdell (1987)]

概述

Scheme 由 G.J. Sussman 和 G.L. Steele Jr. (1975)在 MIT 設計和實(shí)現,作為理解計算的演員模型努力的一部分[Hewitt (1977)]。它是小而緊湊的語(yǔ)言,但是它的概念可以按高度正交性的方式組合和擴展。

C-Scheme [MIT (1984)] 是最初 MIT 實(shí)現的最新版本。Scheme 311 [Clinger (1984)] 在 Indiana 大學(xué)開(kāi)發(fā)并最終演化成 Chez Scheme [Dybvig and Smith (1985)]。Scheme 84 [Friedman et al. (1984)] 是  Indiana 大學(xué)的另一個(gè) Scheme 計劃,主要用于實(shí)驗新穎的控制結構;比如“引擎”概念。T [Slade (1987), Rees et al. (1984)] 是對效率由特殊強調的一個(gè)擴展的 Scheme 系統。它是在 Yale 大學(xué)開(kāi)發(fā)的。所有這些實(shí)現都設計為運行在基于 Unix 的系統上,特別是在 Vax 和 Sun 計算機上。編程通常由標準的編輯器來(lái)支持,比如 Emacs,并有一些簡(jiǎn)單的實(shí)用工具用做錯誤跟蹤和調試。針對個(gè)人工作站的實(shí)現通常提供更加方便的程序環(huán)境。PC-Scheme [Bartley and Jensen (1986), Wong (1987)] 在 Texas Instruments 寫(xiě)成,用于 IBM PCs 和 DOS-兼容微機。它包括一個(gè) Emacs-式樣的編輯器(Edwin)和一個(gè)簡(jiǎn)單的調料包(Scoops)。MacScheme [Semantic Microsystems (1986), Hartheimer (1987)] 為 Apple MacIntosh 提供一個(gè)集成的 Scheme 環(huán)境。

Rees & Clinger (1986) 的“Revised Report on Scheme”擔任了標準,并被推薦為一個(gè)卓越的、簡(jiǎn)明的和非??勺x的語(yǔ)言定義。在本書(shū)中的多數程序用 MacScheme、PC Scheme、ChezScheme 和 T 測試過(guò)了,盡管 MacScheme 被用做主要的開(kāi)發(fā)工具。出于對可移植性的關(guān)心,盡可能的避免了非本質(zhì)特征。

與其他 Lisp 方言形成對比,Scheme 支持顯式的變量定義和詞法作用域。語(yǔ)言提供“尾部遞歸”的高效實(shí)現,并提供“續體(continuation)”來(lái)定義新的控制結構。所有 Scheme 的對象被作為“一等公民”來(lái)對待,這意味這沒(méi)有武斷的限制對象可以做什么和它們要用在那里。任何一類(lèi)對象可以賦值為變量的值,傳遞為給過(guò)程的參數,返回為一個(gè)結果,和存儲在復合數據結構中。依據這個(gè)定義在多數其他語(yǔ)言中過(guò)程被明確的作為“二等公民”對待,包括 Lisp 的早期版本。Clinger (1988b) 提供了對這個(gè)概念的深度討論。

表示和解釋 - 符號 & 值

詞法作用域和一級過(guò)程的組合培養強調模塊性和數據抽象的編程風(fēng)格。Scheme 程序可以看作一組嵌套的表達式,可能引用到一些定義,而這些定義自身可能包含更多的表達式。每個(gè)表達式都關(guān)聯(lián)著(zhù)一個(gè)環(huán)境,它定義“在作用域中”所有綁定。

        (define MeaningOfLife 42)        (+ MeaningOfLife 7)

例子是有兩個(gè)表達式的一個(gè)“程序”,其中第一個(gè)表達式把系統提供的 define 過(guò)程應用到一個(gè)標識符和作為它的參數的一個(gè)值。

在下面的討論中,我們將使用粗體字來(lái)標記被系統以某種預先定義的方式使用的所有標識符。對所有的系統響應前導上一個(gè)分號(;)。在我們把精力轉移到這個(gè)程序的執行之前,還要注意一些其他事情。Scheme 對標識符不做長(cháng)度限制。它們必須開(kāi)始于不能開(kāi)始數字的一個(gè)字符,并可以包含任意樹(shù)木的字母、數字和所謂的“擴展字母表字符”字符。* / < = > ! ? : $ % _ & ~ ^ 被作為這種擴展字母表字符對待。一些標識符充當語(yǔ)法關(guān)鍵字而不應當用做變量。注釋開(kāi)始于一個(gè) ; 并延伸到行結束。它們被解釋器所忽略。還有一個(gè)語(yǔ)法慣例,所有重新綁定變量的過(guò)程應當以 ! 結束,而以 ? 結束的所有過(guò)程都返回 #t 或 #f (用做“真”和“假”)。

Scheme 解釋器以 Lisp 的經(jīng)典的“讀,求值,打印”周期的方式操作;就是說(shuō),它將讀一個(gè)表達式,嘗試求值它,并顯示結果。在執行期間假定,除非被引用,否則把原子求值為它們的綁定,并且表結構指示函數對參數的“應用” (所以叫“應用式語(yǔ)言”)。依據“Cambridge Polish”的慣例解釋器將嘗試對表的第一個(gè)元素關(guān)聯(lián)上一個(gè)過(guò)程,把它應用到所有隨后的參數上。

與解釋器的交互通常在某種支持編程的環(huán)境中通過(guò)所謂的記錄器(transcript)進(jìn)行。在這里鍵入表達式并打印解釋器的回應。通常提供編輯器層次的語(yǔ)法支持;比如,系統將按照鍵入適當的縮進(jìn)定義,并且“閃爍”匹配的圓括號。這種特征對于有如此稀少語(yǔ)法的語(yǔ)言是絕對本質(zhì)性的。

表達式可以被鍵入和求值,還可以滾動(dòng)窗口的內容來(lái)查看前面交互的結果。這種外在形式(figure)也示范了 MacScheme 的 3 類(lèi)菜單。File 菜單涉及建立、裝載、更新和打印文件的動(dòng)作,還用于退出系統。Edit 菜單與鼠標驅動(dòng)的編輯器交互。它提供用來(lái)剪切和粘貼的命令,還有用來(lái)選擇一個(gè)表達式“直到”匹配的左括號("pick")和強制重新縮進(jìn)(表達式通常按照它們鍵入的樣子來(lái)縮進(jìn))。Command 菜單特定于 MacScheme,而其他兩個(gè)菜單類(lèi)似于其他 MacIntosh 軟件的 File 和 Edit 菜單。有用來(lái)求值被選擇了的表達式,和用來(lái)打斷和繼續執行的命令?!斑x擇”可以使用 "pick" (在 Edit 菜單中),也可以通過(guò)在光標已經(jīng)定位于表達式的最右圓括號之后按住“enter”來(lái)完成。打斷一個(gè)過(guò)程的執行將調用一個(gè)調試器,而 "pause"將簡(jiǎn)單的停止這個(gè)程序。在已經(jīng)修正了錯誤條件之后,可以使用 "Reset" 來(lái)返回控制到“頂層”來(lái)繼續一個(gè)會(huì )話(huà)。MacScheme 允許我們打開(kāi)最多 4 個(gè)獨立窗口。其中之一將總是記錄器,而其他可以提供對文件內容的編輯器。表達式的選擇和求值也可以發(fā)生在這種窗口中。表達式將接著(zhù)被復制到記錄器中而結果值將在記錄器中顯示)。這通常會(huì )導致在記錄器中的文本增長(cháng)的非常長(cháng),所以它的內容應當不時(shí)的被“修整”。最后,因為標準的 MacIntosh 屏幕非常小,有一個(gè)命令當記錄器被其他窗口掩蓋的時(shí)候用來(lái)顯示它。其他 Scheme 方言也提供類(lèi)似的、但經(jīng)常不是很徹底的東西,用做方便的程序員界面。

在上述程序被求值的時(shí)候,它將導致建立一個(gè)環(huán)境,MeaningOfLife 在其中綁定(到 42)。隨后的表達式將接著(zhù)在這個(gè)環(huán)境的作用域內進(jìn)行一次加法,返回值“42”。還要注意所有表達式都必須產(chǎn)生一個(gè)值,它總是要打印出來(lái)。它通常是被求值的最后一個(gè)項目的值,當然,有時(shí)這不是想要的行為;就是說(shuō),我們可能經(jīng)常希望強制解釋器按字面接受某些數據??梢允褂?quote 函數來(lái)獲得這種效果,比如

        (quote (define MeaningOfLife 42))        ; (define MeaningOfLife 42)

因為引用是極其常見(jiàn)的操作,提供了一個(gè)簡(jiǎn)寫(xiě)。用來(lái)減少大量的圓括號??梢允褂靡粋€(gè)前置(prime)的 (‘)來(lái)指示引用。

        ‘(define MeaningOfLife 42)        ;(define MeaningOfLife 42)

當然,還應當有取消這種引用的方法。Lisp 的 eval 提供這種功能。與 Lisp 不同的是,這個(gè)函數不被很多 Scheme 方言所支持。這反映了使用 eval 使有選擇的代碼連接很困難的事實(shí),甚至對于單獨的應用程序是不可能的,而要求在執行時(shí)獲得“完全的” Scheme 系統。多數時(shí)候它都不是必須的,因為通??梢杂貌煌姆椒ㄍ瓿赏瑯拥男Ч?。在 MacScheme 中能獲得 eval,但是我們要克制對它的使用。不恰當的使用引用和求值是程序錯誤的非常多產(chǎn)的來(lái)源,它很快就會(huì )成為對于所有初學(xué)者顯見(jiàn)的痛苦。它對增值這些操作建造于其上的基本模型是重要的。如同匯編語(yǔ)言,Scheme 在數據和程序之間不加以語(yǔ)法上的區別。只在指定的解釋上下文中決定符號的“意義”。符號要么在字面上要么通過(guò)關(guān)聯(lián)(association)指示(denote)對象。在執行期間將獲得符號的綁定,而數值、邏輯值、字符、字符串和向量求值為自身。

數據類(lèi)型和它們的操作

Scheme 提供的文字類(lèi)型有數值、邏輯值、字符、字符串、符號、表、向量和 lambda 表達式。

Scheme 提供一些簡(jiǎn)單的過(guò)程用于輸入/輸出操作。表達式可以裝載自文件,并在這個(gè)過(guò)程期間發(fā)生求值。在必須用戶(hù)交互的編程任務(wù)中可以使用 read 函數。display 將打印任何對象的值,在需要換行(line-feed)的地方可以使用換行符(newline)。

        (define LuckyNumber (read)) ; 鍵入 7        ; LuckyNumber        LuckyNumber        ; 7        (display LuckyNumber)        ; 7 #t

響應最后一個(gè)表達式的 #t 是 display 返回的值。"7" 的打印作為副作用發(fā)生。盡管不是標準特征,我們將使用兩個(gè)額外的打印過(guò)程來(lái)試圖精簡(jiǎn)與我們的程序輸出有關(guān)的大批代碼。DisplayList 和 DisplayLine 接受任意數目的參數。DisplayLine 還在結束處進(jìn)行一次換行。這兩個(gè)過(guò)程都是系統工具箱的一部分。

        (DisplayLine LuckyNumber "is not" 13)       ; 7 is not 13 #t

Scheme 支持三種不同的對等價(jià)的測試,它們可以應用于任何對象。eq? 定義等價(jià)的最強解釋。它在 Scheme 完全不能以任何方式區別兩個(gè)對象的時(shí)候返回真。eqv? 檢查兩個(gè)對象在“操作等價(jià)”概念上的是否一致[Rees and Clinger (1986), 13]。在多數實(shí)際上有關(guān)的情況下結果與 eq? 相同。equal? 實(shí)現一個(gè)更弱等價(jià)概念,如果對象“打印同樣的東西”則為等價(jià)。

   (eqv?   (list ‘a(chǎn)) (list ‘a(chǎn))) ; 它們 "看起來(lái)一樣",但是   ;()   (eq?    (list ‘a(chǎn)) (list ‘a(chǎn))) ; 存儲在不同的內存單元中。   ;()   (equal? (list ‘a(chǎn)) (list ‘a(chǎn)))   ;#t Scheme 識別整數、實(shí)數和復數,可以按習慣的方式去寫(xiě):   42   ;42   3.14   ;3.14   1.0e10   ;10000000000

提供了多數的謂詞(number?, zero?, positive?, negative?, odd?, even?),比較(=, <, <=, >, >=),算術(shù)(+, -, *, /, remainder, exp, expt, sqrt, log, floor, ceiling, round, truncate, ...),和三角(sin, cos, ...)函數,它們都帶有明顯的行為。

        (number? 7)        ;#t        (odd? 3.14)        ;ERROR: Non-integer argument to function - 3.14        (/ 3 2)        ;1.5        (expt 2 3)        ;8        (floor 3.14)        ;3        (round  7.7)        ;8        (= 7 13)        ;()

在 Scheme 的多數實(shí)現中空表等同于 #f,這解釋了為什么上面的例子返回空表。我們還可以選擇最大值和或最小值,提供了很多到其他表示形式的變換(integer->char, number->string, ...)。

        (number->string (sqrt 3.14))        ;"1.772"

這個(gè)例子還展示了表達式是可以被嵌入的,在這種情況以正常方式(“從內至外”并且“從左至右”)進(jìn)行求值。優(yōu)先級規則明顯的是沒(méi)有必要的,因為 Cambridge Polish 表示法強制我們總是提供完整的圓括號表達式。

#t 和 #f 是兩個(gè)“一般的”邏輯值。出于方便任何值都可以在條件測試中用做邏輯值。在這個(gè)上下文中不是 #f 和空表的所有值都被認為是“真”。提供了一個(gè)謂詞(boolean?)和一些邏輯操作(and, or, not)

        (boolean? ‘())        ;#t        (and #t #t)        ;#t        (or #f (< 7 0))        ;()

字符是表示可打印的字符的對象,使用記號 # 作為前導。例如

        #x        ;120        #y        ;121        #space        ;32

這里的返回的數值是字符的 ASCII 編碼等價(jià)。提供了謂詞(char?)、一些比較(char=?, char>?, ...)和一些轉換(char->integer, integer->char)過(guò)程。

字符串是包圍在雙引號(")內的字符序列。使用反斜杠()來(lái)在字符串中包含雙引號,例如

        (display "the " character delimits a string")        ;the " character delimits a string

要在字符串中包含反斜杠必須使用另一個(gè)反斜杠來(lái)逃逸它。Scheme 提供各種創(chuàng )建(make-string)、謂詞(string?, string-null?)、比較(string=?, ...)、選擇(string-length, string-ref, substring)、變更(string-append, ...)和轉換(string->list, list->string)操作符。

        (string-length "mumble")        ;6        (string-append "mumble " "mumble")        ;"mumble mumble"        (string-ref "mumble" 4)        ;108

上面的例子展示了字符串索引開(kāi)始于 0,因為 "108" 是 "l" 的 Ascii 碼表示。

不是迄今為止提到了的那些符號必被引用來(lái)強制為文字解釋。

        MeaningOfLife        ;42        (quote MeaningOfLife)        ;MeaningOfLife        (quote X)        ;X        X        ;ERROR: Undefined global variable X

在最后的情況下,解釋器嘗試獲得符號的值,它以前沒(méi)有被綁定。因為不存在這么個(gè)值,程序停止并且可能調用一個(gè)調試器。Scheme 使用 define 來(lái)介入變量并綁定它們到一個(gè)初始值,可以接著(zhù)通過(guò)(使用 set!)副作用改變它。

        (define Capnomancy "study of smoke to determine the future")        ;Capnomancy        (set! MeaningOfLife Capnomancy)        ;MeaningOfLife        MeaningOfLife        ;"study of smoke to determine the future"

現在我們要注意 set! 可以用來(lái)改變一個(gè)符號的值,還要注意 Scheme 的“動(dòng)態(tài)類(lèi)型”本質(zhì)將允許我們把一個(gè)數值綁定(比如MeaningOfLife 的"42")替代為不同數據類(lèi)型的實(shí)例??梢允褂弥^詞(symbol?)和變換過(guò)程(string>symbol)來(lái)操作符號。

        (symbol? (string->symbol "Capnomancy"))        ;#t

表是 Scheme 最重要的結構,因為它們用于“數據”和“程序”二者。明顯的,它們必須被引用來(lái)充當 “Lisp-材料的惰性塊”[Hofstadter (1983)]。

        ‘(HappyMole LittleBear LittleTiger)        ;(HappyMole LittleBear LittleTiger)

是有 3 個(gè)元素的一個(gè)(被引用的)表。去除這些引用是要惹禍的。

        (HappyMole LittleBear LittleTiger)        ;ERROR: undefined global variable LittleTiger

Scheme 將嘗試把表處理為把叫做“HappyMole”的過(guò)程應用到余下的元素。結果的錯誤消息再次指出 MacScheme 雜亂無(wú)序的處理表元素,因此無(wú)法找到對“l(fā)ittleTiger”的任何綁定。即使對所有元素的綁定都存在,求值也可能失敗。

        MeaningOfLife        ;"study of smoke to determine the future"        (‘MeaningOfLife)        ;ERROR: Bad procedure MeaningOfLife        (MeaningOfLife)        ;ERROR: Bad procedure "study of smoke to determine the future"

在第一種情況下我們要求符號的當前綁定,而在第二種情況下我們嘗試“執行”這個(gè)符號,在第三種情況下我們嘗試執行這個(gè)符號的值?;叵肫饋?lái),因為表是未被引用的,Scheme 將把它解釋為一個(gè)“程序”。依據 Cambridge Polish 表示法的規則,它的第一個(gè)元素應當指示一個(gè)過(guò)程。在第二和第三種情況,它實(shí)際引用到一個(gè)名字或一個(gè)數值,它們當然不能被“執行”了。

Scheme 自詡提供操作表的豐富的自帶的過(guò)程。它提供謂詞(pair?, null?)、構造(cons, list, append)、 選擇(length, car, cdr, ... list-ref, list-tail, assoc, member, memq) 和變換(list->string, list->vector, ...)。

表在內部存儲為叫做“點(diǎn)對”的樹(shù)結構中。這種表示可以回溯到第一個(gè) Lisp 解釋器,它建造在 IBM 704 上,它的 36-bit 內存單元可以分解成所謂的“減量”和“地址”兩部分。所以一個(gè)內存單元可以用來(lái)存儲要么一個(gè)表元素要么兩個(gè)指針。接著(zhù)從元素(“地址寄存器的內容” - car)和到子表的引用(“減量寄存器的內容” - cdr)(遞歸的)構造表。cons 充當表構造器。它接受一個(gè)元素和一個(gè)表作為參數并返回一個(gè)新構造的表作為結果。請注意空表(),在表處理操作中充當 0 在整數算術(shù)中的那種作用。你可以通過(guò)加到零來(lái)枚舉所有的數,你也可以同樣的通過(guò)增加到空表來(lái)建造表。這個(gè)過(guò)程經(jīng)常被稱(chēng)為“構造表”。即使多數實(shí)現對待 #f 與空表是一致的,在提及空表的時(shí)候使用 () 而不是 #f 是好習慣,保留 #f 用做“假”值。如果傳遞給 cons 兩個(gè)原子,它將制作一個(gè)“點(diǎn)對”;就是說(shuō),“地址”指針將設置為指向第一個(gè)參數,而“減量”指針指向第二個(gè)參數。在實(shí)際編程中直接操作點(diǎn)對是非常少見(jiàn)的,通常都提供更加方便的操作符用作表的構造。list 將綁定任意多個(gè)參數到一個(gè)表結構中,可以使用 append 聯(lián)接 2 個(gè)表。

        (cons ‘LittleBear ‘LittleTiger) ; 制作一個(gè)“點(diǎn)對”        ;(LittleBear . LittleTiger)        (cons ‘HappyMole (cons ‘LittleBear (cons ‘LittleTiger #f)))         ; 同圖 2.12 中一樣        ;(HappyMole LittleBear LittleTiger)        ; 或者使用更容易的方式:        (list ‘HappyMole ‘LittleBear ‘LittleTiger)        ;(HappyMole LittleBear LittleTiger)        (append ‘(HappyMole LittleBear LittleTiger) ‘(AuntyGoose))        ;(HappyMole LittleBear LittleTiger AuntyGoose)

pair? 檢查它的參數是否是一個(gè)“點(diǎn)對”,還可以用于測試“表身份”(listhood)。

        (pair? ‘(LittleTiger))        ;#t

null? 檢查它的參數是否是空表。

        (null? ())        ;#t

對 #f 做同 () 一樣處置的實(shí)現也返回 #t。

        (null? #f)        ;#t

提供各種過(guò)程支持選擇表的特定元素。car 和 cdr 是其中最重要的。使用這樣的名字只有歷史性的理由,現在已經(jīng)完全不適用了,但是 Lisp 社區堅定的抵制朝向更有意義的任何改變(比如,head 和 tail,或 first 和 last)。當然,car 代表“地址寄存器的內容”而引用在表頭部的元素??梢允褂?cdr(“減量寄存器的內容”)來(lái)取回所有余下元素的子表。請注意 car 總是返回一個(gè)單一元素,而在應用到一個(gè)表的時(shí)候,總是返回一個(gè)表(可能為空)! 嘗試對一個(gè)空表的 cars 或 cdrs 將導致一個(gè)錯誤情況,與嘗試除以零是一樣的。

使用car 和 cdr 的組合可以逐個(gè)處理表的每個(gè)元素。在應用到(被引用的!)表(troll hydra banshee gryphon vampire)的時(shí)候,cdr 將產(chǎn)生表(hydra banshee gryphon vampire),而 car 將返回 troll。要得到第 5 個(gè)元素,我們必須調用 cdr 四次。這將返回表(vampire),這時(shí) car 將接者產(chǎn)生想要的元素。Scheme 允許這些訪(fǎng)問(wèn)函數的聯(lián)接,直到第 4 層。(car (cdr (cdr ( cdr (cdr ‘(troll hydra banshee gryphon vampire)))))) 可寫(xiě)成 (cadddr (cdr ‘(troll hydra banshee gryphon vampire)))。容易造成“口吃”可能是改變 car 和 cdr 名字的一個(gè)勉強的理由。實(shí)際上有更方便的方式來(lái)訪(fǎng)問(wèn)表的第 5 個(gè)元素。list-ref 可用于此目的。

        (length ‘(troll banshee vampire))        ;3        (car ‘(troll banshee vampire))        ;troll        (cdr ‘(troll banshee vampire))        ;(banshee vampire)        (car (cdr (cdr ‘(troll banshee vampire))))        ;vampire        (caddr ‘(troll banshee vampire))        ;vampire        (list-tail ‘(troll banshee vampire) 2)        ;(vampire)        (list-ref ‘(troll banshee vampire) 2)        ;vampire

只有最后兩個(gè)例子值得說(shuō)明。list-tail 通過(guò)除去前面的 n 個(gè)元素來(lái)獲得一個(gè)子表,而 list-ref 返回在指定位置的元素。要注意 list-ref 從零開(kāi)始計數!

還有用于查找表的一些過(guò)程。member 和 memq 將返回它的 car 是一個(gè)指定元素的一個(gè)子表。它們使用不同的過(guò)程(equal? 和 eq?)來(lái)測試等同性。請注意它們返回表的事實(shí)不防礙它們用在(邏輯值)條件中。這是把所有非 nil 值作為“真”對待的慣例派上用場(chǎng)的一種情況?!瓣P(guān)聯(lián)表”是一類(lèi)特殊的表,它存儲“鍵-值”綁定。它們可以被表示為有兩個(gè)元素的表的表,而且 Scheme 提供一個(gè) assoc 過(guò)程來(lái)檢索一個(gè)值,它需要一個(gè)鍵作為參數。

        (member ‘vampire ‘(troll banshee vampire))        ;(vampire)        (member ‘banshee ‘(troll banshee vampire))        ;(banshee vampire)        (assoc  ‘LittleBear ‘((HappyMole vampire)                              (LittleBear banshee)                              (LittleTiger troll)))        ;(LittleBear banshee)

Scheme 還提供一些函數來(lái)在表和字符串之間做轉換。但是,它們只有有限的用處,因為 list->string 只接受字符的表,而 string->list 對一個(gè)字符串的成員生成它們的 ascii 碼等價(jià)的一個(gè)列表。

        (list->string ‘(#b #e #a #r))        ; "bear"        (string->list "bear")        ; (98 101 97 114)

向量是異類(lèi)的結構,它的分量可以被改變并使用開(kāi)始于 0 的整數來(lái)索引。它們的長(cháng)度是它們可以包含的元素的數目,并在建立向量的時(shí)候固定了。有效的索引值位于 0 到(length - 1)范圍內。使用 # 符號指示向量。

        (vector 7 "is one of" ‘(7 13 42) )        ;#(7 "is one of" (7 13 42) )

是有三個(gè)分量的 3 向量: 一個(gè)數值,一個(gè)字符串,和一個(gè)表。要生成這樣的向量。Scheme 提供了創(chuàng )建(make-vector, vector)、謂詞(vector?)、選擇(vector-length, vector-ref)、更換(vector-set!)和轉換(vector->list)過(guò)程。

        (define Friends (make-vector 5))        ;#( () () () () () )        (vector-set! Friends 0 ‘bear)        ;#( bear () () () () )        (vector-length Friends)        ;5        (vector-ref Friends 1)        ;()        (vector-ref Friends 0)        ;bear        (vector->list Friends)        ;(bear () () () ())

在到分量的訪(fǎng)問(wèn)必須被計算出來(lái)的應用中、向量是有用的數據結構;比如,在棋盤(pán)中移動(dòng)是可以運算出來(lái)的。例如,一個(gè)象棋盤(pán)可以存儲在分量是八個(gè)向量的一個(gè)向量,其中每個(gè)向量的八個(gè)分量都可以持有“棋子”或表示空方格。

導引控制流

任何編程語(yǔ)言都需要提供某種設施來(lái)導引程序執行的流程。這種控制結構傳統上迎合三個(gè)種類(lèi):順序、選擇和重復。Scheme 表達式通常順序的求值;最內層的表達式先于在程序外層的表達式。cond、if 和 case 將在多個(gè)可供選擇的執行路徑中選擇某一個(gè),還可以使用 begin 把表達式組合到一個(gè)復合結構中。

        ; 一次"化裝"舞會(huì )        (define party ‘((HappyMole vampire)                        (LittleBear banshee)                        (LittleTiger troll)))        ;party        ;和一個(gè)"猜謎游戲"        (cond         ((equal? (assoc ‘HappyMole party)                  ‘(HappyMole troll))          "moles are trolls")         ((equal? (assoc ‘LittleBear party)                  ‘(LittleBear banshee))          "bears are banshees")         ((equal? (assoc ‘LittleTiger party)                  ‘(LittleTiger troll))          "tigers are trolls")         (else "bad guess ?"))         ;bears are banshees

cond 過(guò)程將接收任何數目的條件-動(dòng)作對,這里的條件將按順序求值,如果非假,則接著(zhù)導致與這個(gè)條件相關(guān)聯(lián)的所有表達式的執行??偸侨绱?,最后一個(gè)表達式生成的值將是 cond 返回的值。與 Pascal 等傳統語(yǔ)言不同,條件的求值是“懶惰的”。這意味著(zhù)只在前面的條件被證實(shí)為假的時(shí)候才進(jìn)行當前的嘗試。一旦找到真條件并且它的相關(guān)表達式已經(jīng)被求值了,cond 立即退出。在上面的例子中對 LittleTiger 的角色的正確猜測將永遠找不到。這里有可選的 else 子句在所有條件都是假的時(shí)候調用。cond 是足夠強力來(lái)描述任何可能的選擇。但是,出于程序員的方便,還是提供了一些額外的選擇函數,因為 cond 對于很多人好象是太復雜了。

        ;查找作為‘舞會(huì )’中第一對舞伴的鍵的 tiger        (if (equal? (caar party) ‘LittleTiger)            (display "tiger is first")            (display "keep looking"))        ;keep looking #t

if 期望三個(gè)表達式: 一個(gè)條件,一個(gè) then 分支和一個(gè) else 分支。如果條件產(chǎn)生假,則條件產(chǎn)生假,則執行第三個(gè)表達式(代表 else 分支),否則執行第二個(gè)表達式(then 分支)??梢允褂?begin 來(lái)定義符合表達式,因為它對于在一個(gè)分支上關(guān)聯(lián)多于一個(gè)表達式經(jīng)常是必須的。

        ;查找作為‘舞會(huì )’中第一對舞伴的鍵的 mole        (if (equal? (caar party) ‘HappyMole)            (begin (DisplayLine "mole is first")                   (display "who‘s next ?"))            (display "keep looking"))        ;mole is first        ;who‘s next ? #t

case 表達式類(lèi)似于對應的 Pascal 控制結構。求值一個(gè)表達式并使用結果的值作為索引,用來(lái)從一組侯選者中做出選擇。每個(gè)侯選者都被描述為選擇子和相關(guān)的一些表達式的一個(gè)表,選擇子(selector)必須是常量。

        (set! age 42)        (case (+ age 1)              ((6    ) ‘excited)              ((21   ) ‘rejoicing)              ((30 40) ‘depressed)              (else ‘indifferent))        ;indifferent        ;Owl 的生日聚會(huì )        (set! luckyFellow ‘owl)        (DisplayList "an ideal pet for" luckyFellow " would be a ")        (display (case luckyFellow                       ((tiger bear mole) "dog")                       ((owl)             "tortoise")                       ((fox)             "snake")                       ((duck elephant)   "goldfish")                       (else "???")) )        ;an ideal pet for owl would be a tortoise

遞歸是在 Scheme 中指定重復的最優(yōu)雅和有效的方式。但是,我們將推遲查看這個(gè)概念直到討論完過(guò)程之后。盡管能用迭代表示的任何東西都可以捕獲到一個(gè)等價(jià)的遞歸公式中,也不管 Scheme 優(yōu)化它的執行以至于在效率上沒(méi)有相關(guān)的損失的事實(shí),我可以使用 do 來(lái)描述迭代執行。在第一眼上,do 好象是某種復雜的構造。它接受一些索引變量,它們開(kāi)始于某個(gè)初始值,并以指定的方式增加它們。測試終止條件,如果它返回假則接著(zhù)求值它的循環(huán)體,如果循環(huán)終止,則求值一個(gè)序列的表達式并返回最后一個(gè)的值。do 的結構被示例如下。

       (do ((  ) ...)           ; 聲明變量           (  ... )           ;-> 跳出           ... )           ; 執行循環(huán)體
        (let ((friends ‘(mole bear tiger)))          (do ((next friends (cdr next)))              ((null? next) "all clean now !")              (DisplayLine "wash " (car next))))        ;wash mole        ;wash bear        ;wash tiger        ;"all clean now !"

let 為循環(huán)的執行定義一個(gè)適當的局部上下文。注意對 friends 的綁定只在 let 的作用域內是可獲得的。很多 Scheme 方言(比如 MacScheme)也為無(wú)限迭代提供了 while 表達式。因為這不是一個(gè)標準特征,我們將限定只在它顯著(zhù)的增加代碼的清晰性的情況下才使用它。它一般很容易被遞歸或 do 所取代。除了跨越表達式的迭代之外,Scheme 還提供了跨越表的所有元素的迭代。

        (for-each display party)        ;(HappyMole vampire) (LittleBear banshee)        (LittleTiger troll) ()        (map abs ‘(1 -2 -3))        ;(1 2 3)

for-each 對一個(gè)表的所有元素應用表達式。典型的用于發(fā)揮這個(gè)過(guò)程產(chǎn)生的副作用的利益。for-each 返回的值是未指定的,在另一方面,把每個(gè)應用的結果寫(xiě)到一個(gè)表中,接著(zhù)返回它。在兩種情況中的過(guò)程都不需要是預先定義的過(guò)程。程序員可以提供 lambda 表達式或他自己命名的過(guò)程。


Lambda 表達式和環(huán)境

Scheme 的執行環(huán)境被表示為所謂的“l(fā)ambda 表達式”。這個(gè)術(shù)語(yǔ)起源于 Church 的 lambda 演算(calculus),它充當 MacCarthy 在符號計算上的某些想法的模型。Lambda 表達式開(kāi)始于一個(gè)關(guān)鍵字“l(fā)ambda”,隨后式一個(gè)(可能為空)參數的列表和一個(gè)表達式序列。當 lambda 表達式應用在正確的上下文中的時(shí)候,這些表達式按順序執行并返回最后的值。

        (lambda () "hi there")        ;#        ( (lambda () "hi there") )        ;"hi there"

在第一個(gè)例子中的 lambda 表達式定義過(guò)程文字。在第二個(gè) lambda 表達式周?chē)b了額外的一對括號強制它執行。Lambda 表達式可以接受固定或可變數目的參數。使用不同的語(yǔ)法慣例來(lái)指出需要那種參數傳遞機制。

        ((lambda (aFriend) ; 函數有一個(gè)形式參數: aFriend            (DisplayLine "hi there " aFriend))         ‘HappyMole) ; 調用時(shí)帶有一個(gè)實(shí)際參數: HappyMole        ;hi there HappyMole #t

這里把由 lambda 表達式表示的過(guò)程應用到作為它的參數的 "HappyMole"上。這生成一個(gè)計算,在其中 DisplayLine 把要求的字符串放置到屏幕上。#t 是最后的顯示返回的值,因此它還是整個(gè) lambda 表達式的返回值。如果你在你自己的平臺上寫(xiě)過(guò)了這個(gè)例子,你最有可能忘記引用 HappyMole,導致 Scheme 抱怨另一個(gè) "unbound variable"。得到正確的引用可能需要很多實(shí)踐,但是不久之后它就會(huì )成為“第二天性”。

Scheme 提供兩種可供選擇的方式來(lái)要求可變數目的實(shí)際參數。

        ; lambda 表達式,調用時(shí)帶有 3 個(gè)實(shí)際參數        ((lambda someFriends  ; 參數周?chē)鷽](méi)有括號 !            (DisplayLine "hi there " someFriends))         ‘mole ‘bear ‘tiger)        ;hi there (mole bear tiger) #t        ; 調用時(shí)帶有 0 個(gè)實(shí)際參數        ((lambda someFriends            (DisplayLine "hi there" someFriends)) )        ;hi there () #t  

下面的所有參數都綁定到一個(gè)表(它可以為空)中并傳遞給 lambda 表達式。請注意我們必須提供一個(gè)不帶任何括號的單一的參數來(lái)導致這種行為。如果我們想要讓我們所有的參數都是可選擇的,可以使用

        ; 一個(gè)強制的和一些可選的參數        ((lambda (aFriend . someMore)            (DisplayLine "hi there " someMore))         ‘mole ‘bear ‘tiger) ; 三個(gè)參數        ;hi there (bear tiger) #t  ; mole 現在被綁定到 "aFriend"        ((lambda (aFriend . someMore)            (DisplayLine "hi there" someMore)))            ; 沒(méi)有參數        ;ERROR: Too few arguments to procedure        ;0 arguments supplied - 1 argument expected

這里的 aFriend 將被綁定為 mole,而所有余下的實(shí)際參數將再次被組合到一個(gè)表中并綁定到 someMore。 當然,我們現在必須提供最少一個(gè)實(shí)際參數。

Scheme 提供一個(gè)謂詞(procedure?)來(lái)測試一個(gè)對象實(shí)際上是否為過(guò)程。apply 在某些情況下也是有用的,它強制一個(gè)過(guò)程在當前上下文中的應用。注意 apply 總是期望一個(gè)表作為它的單一的參數。

        (procedure? (lambda () (display "hello")))        ;#t        (apply (lambda (aFriend)                 (DisplayLine "hello" aFriend))               ‘(mole) )        ;hello mole #t

Lambda 表達式可以包含它們自己的對數據和過(guò)程的“局部”定義。因此它們是主要的的環(huán)境建造塊, Scheme 依據“詞法作用域”的概念而有層次的安排它們。使用這種環(huán)境來(lái)定義在一個(gè)計算期間的所有點(diǎn)上(“作用域內”)都是當前可見(jiàn)的對象,早期的 Lisp 系統只支持“動(dòng)態(tài)作用域”,這是導致無(wú)數非常難于跟蹤的程序錯誤的一個(gè)“特征”。

執行的嵌套上下文

除了嵌套過(guò)程 Scheme 還有一個(gè)額外的構造用來(lái)定義(臨時(shí))環(huán)境。叫做“l(fā)et 表達式”(let、let*、letrec)。生成一種“塊結構”(類(lèi)似于 Algol 60)。let 是一個(gè)特殊的形式,它接受綁定和一個(gè)“塊體”作為參數。在這些綁定定義的上下文中,在塊體中包含的表達式接著(zhù)順序的執行并返回最后一個(gè)的值。let* 假定所有綁定將被順序的建立(從第一個(gè)到最后一個(gè)),而 let 不做這種假定。Letrec 對于相互遞歸的過(guò)程是需要的。

        (let ((seven 7) (thirteen 13))           (DisplayLine (* seven thirteen)) (+ seven thirteen))        ; 91 ; 乘法的結果        ; 20 ; 加法的結果        (let ((aNumber 7)              (twiceThat (* 2 aNumber)))          (display twiceThat) )        ;ERROR: Undefined global variable aNumber        (let* ((aNumber 7)               (twiceThat (* 2 aNumber)))          (display twiceThat) )        ;14 #t

這里我們需要使用 let* 來(lái)使相互依賴(lài)的綁定工作。MacScheme 的 let 顯然的嘗試從右到左的綁定符號,因此在被定義之前就遇到了 aNumber。

過(guò)程 - 定義, 測試, 調試

綁定 lambda 表達式到標識符導出了“命名”過(guò)程的概念。典型的 Scheme 程序將包含大量用戶(hù)定義的過(guò)程,通常組織到必須在程序執行之前裝載的一些文件中。使用 load 過(guò)程完成這個(gè)任務(wù)。

        (load "HardDisk:cookieMonster.scm")

將嘗試裝載并求值在目錄 HardDisk 中的文件 cookieMonster.scm 中的所有表達式。文件操縱的詳情通常特定于特定 Scheme 實(shí)現。但是,習慣于對 Scheme 文件使用后綴“scm”。

我們可以“別名”任何數據類(lèi)型,包括過(guò)程。它可以用來(lái)裁剪系統函數的名字來(lái)適合我們的偏好;帶有額外的一層間接的代價(jià)。我們通常保護重要的系統定義的標識符不被意外的重定義。但是,對于經(jīng)常需要的操作符(比如 car 和 cdr),導致的在執行速度上的損失通常是不可接受的高。

        (define head car)        ;head        (define tail  cdr)        ;tail        (head (tail ‘(vampire banshee troll)))        ;banshee        (set! + *) ; sneaky: redefine ‘+‘ as a multiplication symbol ?        ;ERROR: Integrable procedures may not be redefined

請注意 Scheme 的綁定模型是完全一般性的。在對“被動(dòng)”對象(數據)能做的事情和對“主動(dòng)”對象(過(guò)程)能做的事情之間沒(méi)有區別。例如,我們可以輕易的寫(xiě)返回另一個(gè)過(guò)程作為值的過(guò)程。

        (define GenericMagicLamp           ; 這是一個(gè)我們希望返回的"lambda"表達式           (lambda (noOfWishes)              (define count 0) ;局部變量                 (lambda ()                    (if (< count noOfWishes)                        (begin                          (set! count (+ 1 count))                          ‘Granted)                        ‘(you‘ve had all you‘re going to get !))                  ) ))         ; GenericMagicLamp		

GenericMagicLamp 實(shí)現了阿拉丁神燈的概念,在其中關(guān)押了一個(gè)妖魔并通過(guò)摩擦震動(dòng)來(lái)釋放出來(lái)。出于感激的天性,她將接著(zhù)提供慣例的三個(gè)愿望,通常帶有針對 meta-circularity 的某種保護(就是說(shuō),拒絕能帶給你額外愿望的愿望)。我們的過(guò)程包含對一個(gè)局部變量 count 的聲明,它將被初始化為零并在每次滿(mǎn)足愿望之后增加一。在這一點(diǎn)上,注意到 Scheme 變量都有“無(wú)限的范圍”是很重要的,這意味著(zhù),(例如)與 Pascal 中的其他變量不同,在它們在其中定義的過(guò)程的連續調用之間,它們不會(huì )失去它們的值。因此 Count 將開(kāi)始于為零的一個(gè)值,在第一個(gè)愿望之后保持為一的一個(gè)值,第二次之后是二,在第三次調用之后是三。盡管 GenericMagicLamp 自身不會(huì )滿(mǎn)足任何愿望,但是它可以用做一個(gè)發(fā)生器,用來(lái)構造任意數目的神燈,帶有在創(chuàng )建時(shí)刻定義的愿望數目。這是嵌套在這個(gè)過(guò)程之中的第二個(gè) lambda 表達式的作用。記住這個(gè) Scheme 函數總是返回它們遇到的最后一個(gè)表達式的值。在這里是第二個(gè) lambda 表達式,因此 GenericMagicLamp 是返回另一過(guò)程作為它的調用結果的一個(gè)過(guò)程。

        (define AlladinsLamp (GenericMagicLamp 3))        ;AlladinsLamp

可以用來(lái)制造這么一個(gè)燈(一個(gè)過(guò)程)并讓 AlladinsLamp 充當它的標識符。記住這個(gè)過(guò)程還繼承了在創(chuàng )建發(fā)生的時(shí)候、它在其中建立的環(huán)境中的所有綁定變量是很重要的。Count 因此對 AlladinsLamp 是可以訪(fǎng)問(wèn)的,并且它仍被綁定為零。如果我們現在開(kāi)始摩擦,我們就會(huì )得到我們期望的行為了(這些愿望的“形態(tài)”將保持私有)。

        (AlladinsLamp)        ;granted        (AlladinsLamp)        ;granted        (AlladinsLamp)        ;granted        (AlladinsLamp)        ;"you‘ve had all you‘re going to get !"        (set! count 0)        ;ERROR: Undefined global variable count        ;hard luck  - "count" is  kept private to this lamp object, and can‘t be accessed !     

遞歸

遞歸具體化了自引用的概念,并且它是 Scheme 指定一段代碼的重復執行的最有效的工具。闡明它的一般想法的一個(gè)好例子是眾所周知的一個(gè)漫畫(huà),一個(gè)人拿著(zhù)他自己的照片,在照片種他要年輕幾歲。照片中的人依次拿著(zhù)同樣的照片(照片中的人拿著(zhù)同樣的照片 ...) 一直到“降到最底部”、照片中的人是個(gè)嬰兒。

可以使用遞歸來(lái)以非常幽雅的方式描述結構和處理過(guò)程二者[Roberts (1986), Burge (1975)]。唯一的要求是我們可以對其使用遞歸的、事物的某種程度上的規律性。這種規律性必須包含在元素和它們的內部連接的本質(zhì)中,它引發(fā)直接或間接的線(xiàn)性、層次、或網(wǎng)絡(luò )結構。結構的遞歸描述是常見(jiàn)的。上面提及的照片就是一個(gè)例子。在科學(xué)和自然中很多結構是高度遞歸性的,通過(guò)它們的成長(cháng)過(guò)程來(lái)探索某種規律性,在計算機科學(xué)同樣業(yè)富于這種描述??紤]把二叉樹(shù)規定作為“一個(gè)葉子或一對二叉樹(shù)”;或把表規定為“一個(gè)空表或跟隨著(zhù)一個(gè)表的一個(gè)元素”。表的遞歸本質(zhì)很容易的映射到通常處理它們的遞歸方式上。在圖表 2.13 中展示了一種適用的情況。這里遞歸的調用 cdr 函數來(lái)“走”到我們感興趣的元素那里。處理過(guò)程的遞歸描述在日常生活中也是很流行的。兒童的故事通常是高度遞歸的,而且這種描述好象非常容易理解和非常有趣(在那個(gè)年紀)。我們將查看下面的簡(jiǎn)單的例子。這種故事也非常容易“產(chǎn)生”(講述)而很快變得無(wú)聊。

用數學(xué)歸納法證明的處理過(guò)程與遞歸描述密切相關(guān)。我們可以很非正式的把遞歸定義為把一個(gè)問(wèn)題簡(jiǎn)化成在結構上是一致的、并某種程度上更加容易解決的一個(gè)或多個(gè)子問(wèn)題的處理過(guò)程。這種想法可以接著(zhù)應用到每個(gè)子問(wèn)題,直到整個(gè)處理過(guò)程直到子問(wèn)題的解決變得明顯的的某個(gè)層次。我們可以接著(zhù)“遞歸回溯”子問(wèn)題鏈來(lái)把所有東西再次合到一起,所以對最初問(wèn)題的解決將被構造出來(lái)。Hofstadter (1983) 給出了這種處理過(guò)程的愚蠢但有啟迪意義的一個(gè)例子: “你如何做有 13 個(gè)石塊的一個(gè)堆? - 放置一個(gè)石塊在有 12 個(gè)石塊的一個(gè)堆上。你如何做有 12 個(gè)石塊的一個(gè)堆上? ..."。我們將把任務(wù)的完成留做讀者的練習。當然我們必須總是小心的提供某種方式,能夠預期這種描述或處理過(guò)程終止。想象一下執行了下列程序之后會(huì )發(fā)生什么 [Dybvig (1987), 37] (它的名字告訴我們不要嘗試它)。

        (define GoodBye (lambda () (display ".") (GoodBye)))        (GoodBye)

可以用迭代表示的所有東西都可以捕獲到等價(jià)的遞歸公式中。與其他語(yǔ)言不同的是在 Scheme 中使用遞歸經(jīng)常沒(méi)有存儲空間的處罰,因為解釋器將自動(dòng)的嘗試把遞歸描述轉換到一個(gè)迭代循環(huán)結構中。這總是由所謂的“尾部遞歸”來(lái)保證。如果在遞歸調用點(diǎn)上的一個(gè)過(guò)程的值是這個(gè)遞歸調用的值,則這個(gè)調用是尾部遞歸的。但是,如果在把這個(gè)結果“向上”傳遞回遞歸鏈之前對它進(jìn)行了某些操作,則尾部遞歸不成立[Dybvig (1987), 71]。在需要對表元素的操作的重復性應用的時(shí)候,還有在其他通常使用重復結構(比如 do, for, while)的上下文中,尾部遞歸發(fā)生的非常頻繁。因此 Scheme 的迭代過(guò)程只是充當語(yǔ)法糖衣。

在 Scheme 中遞歸最通常用于表處理??紤]一個(gè)程序,寫(xiě)眾所周知的“Hairy MacLary”類(lèi)型的兒童故事的[Dodd (undated)]一個(gè)變種。在這些故事中 Hairy McLary(一個(gè) terrier 狗)出去散步并沿途“積累”朋友,所以在每個(gè)門(mén)口唱誦的名字表將逐漸變長(cháng)。

        (define StoryTeller          (lambda (mainCharacter someFriends)            (define chorus               (lambda (someFriends caravan)                  ; 檢查終止                  (if (null? someFriends)                      #f                      (begin                        ; 問(wèn)候一個(gè)朋友                        (DisplayLine (car someFriends))                        ; 并“羅列”所有已經(jīng)加入的人                        (display "with ")                        (map (lambda (aFriend)                                (DisplayLine aFriend)                                (display "and "))                             caravan)                        (DisplayLine "...")                        ; 在另一個(gè)朋友上遞歸(并讓這個(gè)人                        ; 加入 "caravan")                        (chorus (cdr someFriends)                                (append                                   caravan                                   ; needs a list here !                                  (list (car someFriends))) ))) ))        ; "StoryTeller" 函數的函數體         (DisplayLine "out of the gate and off for a walk went")         (DisplayLine mainCharacter " ...")         (newline)         (chorus someFriends (list mainCharacter))         ; we will skip some more friends here, and also their         ; encounter with Scarface Claw (the toughest Tom in town)         (newline)         (display "straight back home to bed") ))         ;StoryTeller

在做調用帶有如下數據的時(shí)候,這個(gè)尾部遞歸過(guò)程生成下面的熟悉的故事:

        (StoryTeller         "Hairy MacLary from Donaldson‘s Dairy"         ‘("Hercules Morse, as big as a horse"           "Bottomley Potts, covered in spots"           "Muffin McLay, like a bundle of hay"))           ; out of the gate and off for a walk went           ; Hairy MacLary from Donaldson‘s Dairy ...           ; Hercules Morse, as big as a horse           ; with Hairy MacLary from Donaldson‘s Dairy           ; and ...           ; Bottomley Potts, covered in spots           ; with Hercules Morse, as big as a horse           ; and Hairy MacLary from Donaldson‘s Dairy           ; and ...           ; Muffin McLay, like a bundle of hay           ; with Bottomley Potts, covered in spots           ; and Hercules Morse, as big as a horse           ; and Hairy MacLary from Donaldson‘s Dairy           ; and ...           ; straight back home to bed#t

上面的程序是尾部遞歸的,因為我們在后續對 chorus 的調用返回之后不嘗試更改它生成的任何數據。它還對展示非尾部遞歸的過(guò)程是有教益的。

這次我們的朋友們攢錢(qián)買(mǎi)一個(gè)新沙發(fā),他們定時(shí)地希望總計他們的錢(qián)看是否已經(jīng)買(mǎi)得起了。下面的過(guò)程做這個(gè)把戲,假定我們列出朋友和有關(guān)的財產(chǎn)。

        (define HowMuchMoneyDoWeHave          (lambda (someFriends)           (define count              (lambda (aFriend) (cadr aFriend)))           (trace count)           ; "HowMuchMoneyDoWeHave"過(guò)程的過(guò)程體           (if (null? someFriends) ; 終止條件               0               ; 把它們加到一起               (+ (count (car someFriends)) ; 這個(gè)人                  (HowMuchMoneyDoWeHave ((cdr someFriends))) ; 其他人的            ) ))         ;HowMuchMoneyDoWeHave         (trace HowMuchMoneyDoWeHave)          ; #t

下面的跟蹤展示在程序執行期間遞歸是如何展開(kāi)的。跟蹤功能不是標準的 Scheme 特征,但多數方言都提供了這種設施。

      (HowMuchMoneyDoWeHave         ‘((HappyMole 3) (LittleBear 1) (LittleTiger 0)))

將打印:

        ; 遞歸“進(jìn)入”表            Computing (#                       ((happymole 3) (littlebear 1) (littletiger 0)))                 Computing (#                            ((littlebear 1) (littletiger 0)))                    Computing (#                               ((littletiger 0)))                       Computing (#                                  ())                  ; 終止條件滿(mǎn)足了,                  ; “回繞”這個(gè)遞歸 (做我們要的總計)                  (# ()) --> 0               Computing (# (littletiger 0))                 (# (littletiger 0)) --> 0                         (#                                 ((littletiger 0))) --> 0                 Computing (# (littlebear 1))                   (# (littlebear 1)) --> 1                 (#                      ((littlebear 1) (littletiger 0))) --> 1                   Computing (# (happymole 3))                  (# (happymole 3)) --> 3                                (#                     ((happymole 3) (littlebear 1) (littletiger 0)))                      --> 4            ; 4

運氣不好,沙發(fā)價(jià)值 10 個(gè)金幣,他們仍不能買(mǎi)一個(gè)。如同預想的那樣你不能太依賴(lài) tiger。他對金錢(qián)沒(méi)有什么想法。

額外特征

本節總結對 Scheme 的簡(jiǎn)要介紹。盡管多數 Scheme 的主要特征已經(jīng)被覆蓋了,沒(méi)有空間做更多的風(fēng)格討論了。我們故意把我們的處置限制在據信是 Scheme 的本質(zhì)的和可移植的那些方面。Rees 和 Clinger (1986)給出這門(mén)語(yǔ)言的完整定義。有些額外的標準特征,特別是在數值和輸入/輸出操作領(lǐng)域中。概念,比如引擎[Dybvig (1987),Hayes 和 Friedman (1984)],宏和語(yǔ)法擴展[Dybvig (1987)]沒(méi)有提及;主要是因為仍然缺乏這些概念的標準實(shí)現,盡管很多方言已經(jīng)提供了一些這種設施。請注意本書(shū)不意圖充當參考手冊的替代品。因此我們強烈建議使用你工作用的方言的參考手冊,接著(zhù)繼續你的探索。

編程是需要實(shí)踐的一種技巧,特別是對于“良好”風(fēng)格的開(kāi)發(fā)。讀其他人寫(xiě)的程序是特別重要的,然而寫(xiě)你自己的程序和從你的錯誤中學(xué)習是生死攸關(guān)的。

Scheme 好象特別適合于這種業(yè)務(wù),因為它帶給用戶(hù)的不只是編碼解決某些問(wèn)題,還有尋找特別優(yōu)雅的公式表達。Friedman 評論一個(gè)良好的計算機語(yǔ)言為:“應當提供一個(gè)環(huán)境,程序可以在其中為手頭的問(wèn)題創(chuàng )造性生成計算模型或范例。程序員構造范例的能力不應當受到限制。我把易于構造范例稱(chēng)為語(yǔ)言的“易范例性”(paradigmicity)。有低易范例性的語(yǔ)言是討厭的。...我們如何建立有高易范例性的語(yǔ)言呢? 我們介入一些基本概念,組合這些概念的一些方式,并且這么做的過(guò)程中我們利用了解決問(wèn)題的多年經(jīng)驗。在現存的語(yǔ)言中沒(méi)有那個(gè)比 Scheme 更易范例性了。它的基本概念是過(guò)程、續體、引擎、條件和賦值語(yǔ)句。所有東西都會(huì )合在復合和遞歸下,它預備了語(yǔ)法和語(yǔ)義擴展二者。... 使用這些基本概念的實(shí)驗和實(shí)踐的越多,新范例出現的越快?!?[Friedman in: Dybvig (1987), preface]。

對風(fēng)格的一些建議

Scheme 是一個(gè)高度交互式的語(yǔ)言,它帶有鼓勵試探性風(fēng)格的問(wèn)題解決的特征。這種屬性使學(xué)習過(guò)程變得容易。新的想法可以立即實(shí)驗,它擔當使困難的概念非神秘化的任務(wù)要比任何“靜態(tài)”的描述形式要好。表結構用來(lái)表示數據和程序二者。因為它們還強調了有層次的嵌套和遞歸,這鼓勵了“塊狀”的應用程序和創(chuàng )造分層設計。

有一些基本規則用來(lái)寫(xiě)有良好結構的 Scheme 程序,多數這些規則類(lèi)似于在典型的 Lisp 教科書(shū)中給出的指導方針。Abelson 等人(1985) 的書(shū)和在本書(shū)中討論的工具箱程序可以充當有代表性的例子。

過(guò)程定義應當簡(jiǎn)要,并且它們應當面向一個(gè)單一的、定義良好的目的。任務(wù)應當盡可能的分解到一些子任務(wù)中,如果它們延伸超過(guò)了一頁(yè),把它們委派給其他過(guò)程。過(guò)程還應當圍繞概念來(lái)組織,概念依次反映在正確的數據結構中。這個(gè)要求演化自分層設計的方法論,強烈建議使用它。創(chuàng )建、查詢(xún)、選擇、顯示和更改過(guò)程的概念給出對通常需要那種過(guò)程的指導。

程序應當是可讀的和易于理解的。這個(gè)原理對標識符命名和程序構造方式有明顯暗示。標識符應當是描述性的,并且它們應當提供與所表示的概念有關(guān)的助記符。還希望你遵從分類(lèi)標識符的特定格式慣例。例如,我們使用大寫(xiě)字母來(lái)開(kāi)始工具箱過(guò)程的名字,而小寫(xiě)字母用作系統定義的過(guò)程的名字。Scheme 使用特殊的后綴: "?" 用于謂詞(就是返回 #t 或 #f 的函數)和 "!" 用于有“副作用”的所有過(guò)程(就是說(shuō),改變對非局部變量的綁定)。因為 Scheme 是一個(gè)動(dòng)態(tài)類(lèi)型的語(yǔ)言,參數的命名應當反映它們的值。使用 aNumber 或 anAList 替代 n 或 l。盡管你可能開(kāi)始時(shí)憎恨鍵入長(cháng)名字、并可能感覺(jué)你的程序過(guò)于冗長(cháng)了,額外的努力以后會(huì )得到報答的。

在你的程序中使用清晰的結構將要求某些決斷力,而你將最終發(fā)展出一種你自己的風(fēng)格。完成這個(gè)目標的唯一方法是閱讀和評判其他人的代碼,并采納你喜歡的到你寫(xiě)的程序中。因為這種決定通常涉及感性,除了維持一致性之外,不能給出一般的規則。不要懼怕實(shí)驗。如果你覺(jué)得增進(jìn)了的理解能使你做出更好的工作,拋棄老的程序并再次開(kāi)始。

遞歸和表結構在編程符號應用中同迭代和數組在數值應用中一樣常見(jiàn)。盡管你需要做有意識的努力來(lái)克服所有 Pascal 或 Fortran 條件反射,你應該使用它們發(fā)揮它們的全部?jì)?yōu)勢。遞歸是定義和處理重復和有規律結構的“自然的”和優(yōu)雅的方式,并且這種結構很易于表示為表。

set! 過(guò)程可能產(chǎn)生“非局部”的副作用,所以它有害于一個(gè)程序的可理解性。這導致一些人完全反對它的使用并鼓吹沒(méi)有任何副作用的“純”函數式編程。在處理過(guò)程要按照復雜的狀態(tài)變化來(lái)描述的情況下,這種風(fēng)格經(jīng)常是非常不實(shí)際的,所以作者不贊同這種推理。改變值綁定的能力是非常有用的工具,它的作用不能總是很方便的用任何其他方式完成。你應當盡可能的細心的保持這種綁定的作用域為局部,并適當的加以文檔。在很多情況下,let 表達式、嵌套過(guò)程調用和遞歸能導致更清晰的結構。

深度嵌套的 cars 和 cdrs 經(jīng)常是難于理解的,因此應當避免。處理這種深度遞歸結構的更加可取的方法是使用多層解釋?zhuān)@樣 car 和 cdr 的長(cháng)鏈就變得不需要了。例如,(GetXCoordinate (GetFirstPoint (GetBottomEdge (aTriangle)))) 比 (caar (caddr aTriangle)))更可取。使用適當的選擇子(selector)函數也會(huì )使你的程序更加靈活并更易于維護。對象封裝的實(shí)質(zhì)性利益也應當探究,因為使用這種象征(metaphor)有助于使你的程序更加健壯和安全。

象很多其他強力特征一樣,續體可能是危險的工具。盡管它們在某種程度上比“go to”語(yǔ)句相關(guān)的想法更加“安全”,它們的正確使用在相當大程度上要求程序員方面的自我約束。對“why”、“what”、“where”和“how”加以適當的文檔也是基本的,更甚于更簡(jiǎn)單的結構。因此續體應當保守的使用,用來(lái)實(shí)現其他方式不能實(shí)現的目標。即使你不應當以它們的“原始”形式使用它們,但是應當把它們看作組合適當的高層結構的建造塊(比如,非局部 escape, 協(xié)同例程 ...)。

Scheme 的交互式本質(zhì)允許你在過(guò)程寫(xiě)完之后立即測試它們。你應當完全利用這種可能性并運行一組選擇的測試個(gè)例,有效的和無(wú)效的二者都要有。

在編程風(fēng)格上的多數一般指導方針也適用于 Scheme 程序。Kernighan & Plauger (1974) 和 Ledgard (1974) 給出了一個(gè)良好的總結。

程序應當適當的縮進(jìn)和注釋?zhuān)M管使用適當的標識符和有可能迅速的測試過(guò)程減少了注釋的數量但它們仍是需要的。過(guò)程的定義通常應當開(kāi)始于一個(gè)對它功能的簡(jiǎn)要解釋?zhuān)绻麖乃拿挚磥?lái)不是很明顯的。在復雜的應用中還應當有對類(lèi)型、結構和這個(gè)標識符用途的描述。除了這些考慮之外,應當使用注釋來(lái)突出一段代碼重要的部分。它們應當經(jīng)常是解釋性的(就是說(shuō),在做“什么”)而不是純描述性(就是說(shuō),是“如何”完成的)??s進(jìn)應當是一致的并應當強調程序的結構。因為嵌套的過(guò)程的清單有著(zhù)一種不幸的傾向,它會(huì )溢出頁(yè)面的右側,在任何 scheme 縮進(jìn)之下,都經(jīng)常需要行使某些調整,在一致性和美觀(guān)性之間作出權衡。

所有信息都應當盡可能的模塊化和局部化。全局變量應當非常保守的使用! Scheme 支持局部變量和局部過(guò)程,你應當使用這些設施來(lái)把信息裝載到正確的模塊中。例如,任何只在另一個(gè)過(guò)程內部調用的過(guò)程都應當嵌套到那個(gè)過(guò)程中。作為它的邏輯上的結論,這導致了面向對象結構,盡管有些事例中過(guò)程是簡(jiǎn)單的,在其中使用與面向對象有關(guān)的額外的“腳手架”是不正當的。

因為 Scheme 用作討論很多重要的編程象征的一個(gè)工具,本書(shū)后面的章節將提供給出近一步提示和察看這些建議應用的充分機會(huì )。

總結和看法

Lisp 是圍繞符號操縱的想法建造的。原子和表是它的基本結構建造塊。每個(gè)對象要么是一個(gè)表要么是一個(gè)原子。表可以遞歸的定義,所以可以有表的表的表 ...,形成任意復雜的樹(shù)結構,表和原子可以被求值為要么數據要么是所謂的 lambda 表達式。事實(shí)上,Lisp 解釋器將求值它遇到的所有東西,除非被顯式的引用了。

Lisp 是一個(gè)應用式和基于表達式的語(yǔ)言,這意味著(zhù)解釋的主體單元是圓括號表達式?!凹儭?Lisp 禁止“副作用”。這個(gè)限制經(jīng)常被證明是過(guò)分限制、并且實(shí)際語(yǔ)言在很大程度上屏棄了它。所以 Lisp 通常不能被當作函數式語(yǔ)言。需要介入賦值的正確決斷必須處理好模塊化的問(wèn)題,這超出了本文的范圍。Abelson 等人(1985) 的書(shū)的第三章是詳細分析的好來(lái)源。Lisp 表達式可以引用其他表達式,要么直接的要么通過(guò)把某種函數應用到參數。每個(gè)對象,包括函數,可以動(dòng)態(tài)的構造和操縱;所以我們可以有返回函數的函數,改變函數定義的函數,等等。存儲管理是自動(dòng)進(jìn)行的。對對象沒(méi)有類(lèi)型限制,但程序員可以通過(guò)選擇適當的標適符來(lái)細心的介入(數據)結構。盡管為變量聲明關(guān)聯(lián)上數據類(lèi)型通常被認可為編譯型編程語(yǔ)言的重要特征,它在解釋性環(huán)境沒(méi)有多大用處。它的主要用處在于使編譯器可以?xún)?yōu)化存儲和執行,并對程序做一致性檢查。解釋器動(dòng)態(tài)的建立數據結構,所有優(yōu)化并不重要。自動(dòng)的一致性檢查可能使需要的,但是因為程序員在程序失敗的時(shí)候有權訪(fǎng)問(wèn)所有綁定的來(lái)源和值,錯誤預防通常不象編譯環(huán)境中那么關(guān)鍵。它可以被高級的錯誤檢查和更正特征所取代。當然,效驗和優(yōu)化一個(gè)已經(jīng)穩定了的程序仍使非常需要的。

遞歸和條件表達式合起來(lái)是 Lisp 的主要控制結構。這是恰當的,因為它允許容易的和優(yōu)雅的定義重復的規則的結構。在常規計算機體系上遞歸的低效一直是對 Lisp 的主要批評之一。盡管這種討論直到幾年前還是有益的,現在在很大程度已經(jīng)被新的實(shí)現技術(shù)和專(zhuān)用“Lisp 機器”的出現所克服,Lisp 機器在微程序級別支持表和遞歸,但是,仍然可以公平的說(shuō)使用 Lisp 經(jīng)常會(huì )導致“內存饑荒”的程序。

Lisp 是一個(gè)交互語(yǔ)言,支持試探風(fēng)格的程序開(kāi)發(fā)。結構編輯器和復雜的調試工具通常作為它所嵌入的編程環(huán)境的一部分。這使編寫(xiě) Lisp 函數的任務(wù)非常容易,而不用管眾所周知的那些錯綜復雜的圓括號。結構編輯器可以幫助圓括號恐懼癥患者戰勝 Lisp 稀少的語(yǔ)法帶來(lái)的多數缺點(diǎn)。

很多其他經(jīng)常引證的缺點(diǎn)(比如,動(dòng)態(tài)作用域,缺乏“塊結構”,單一的數據類(lèi)型,低效的數值操作和計算效率)已經(jīng)在新方言中以各種方式去除了。

盡管很多年過(guò)去了,仍然只有不多的講授用 Lisp 編程的教科書(shū),這種情況最近已經(jīng)戲劇性的改變了?,F在已經(jīng)有了針對不同方言的、對不同背景的讀者廣泛的教科書(shū)。Siklossy(1976) 的書(shū)使最老的一本。它很大程度上限制自身到 Lisp 1.5 已經(jīng)介入的那些特征。Allan(1978) 仍是一本出色的技術(shù)性介紹,使用“M-表示法”使它很大程度上獨立于任何特定方言,它還包含了對實(shí)現要點(diǎn)的一個(gè)可靠的討論。但是 Allen 的教科書(shū)對沒(méi)有先前的編程經(jīng)驗的學(xué)生可能是不可企及的,Touretzki(1984) 和 Hasemer(1984) 寫(xiě)的書(shū)明確的針對這種“初學(xué)者”。Wilensky (1984) 是更加面向技術(shù)的另一個(gè)有趣的教科書(shū)。這本書(shū)有兩個(gè)版本可獲得;一本基于 FranzLisp 而另一本基于 Common Lisp。Winston 和 Horn(1984) 強調 AI 應用。他們的書(shū)是 Winston(1984) 的伙伴并且被重寫(xiě)了來(lái)確保 Common Lisp 兼容。Tanimoto(1987) 是關(guān)于 AI 方法論的一個(gè)新近的教科書(shū)。同很多這種教科書(shū)一樣它包含一個(gè)優(yōu)秀的、但必須是簡(jiǎn)要的對 Lisp 的介紹。最后, Charniak 等(1980)寫(xiě)一本教科書(shū),基于 UCI Lisp,對于 AI 編程技術(shù)給予高級的對待,它的一個(gè)新版不久就會(huì )出版。Abelson、Sussman 和 Sussman(1985) 仍是關(guān)于 Scheme 的最重要的教科書(shū)。Friedman 和 Felleisen(1986) 和 Eisenberg(1987) 提供一個(gè)要求更少的介紹。Dybvig(1987),ChezScheme 的開(kāi)發(fā)者,寫(xiě)帶有一些更充實(shí)的例子的一本教科書(shū),在其中他還覆蓋了如引擎和語(yǔ)義擴展這樣的特征。有一個(gè)活躍的 Scheme 用戶(hù)組通過(guò)在 MIT(uucp: Scheme@mc.lcs.mit.edu)的郵件列表交流。



本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Lisp語(yǔ)言一些看法
InfoQ: 探索JVM上的LISP
Lisp 已死,Lisp 萬(wàn)歲!
跨越邊界: Lisp 之美
我的Python學(xué)習之路十四:函數(三)
Lambda 表達式編寫(xiě)遞歸一:前言及基礎
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久