LISP:修订间差异

维基百科,自由的百科全书
删除的内容 添加的内容
第16行: 第16行:
| dialects = [[Arc]], [[AutoLISP]], [[Clojure]], [[Common Lisp]], [[Emacs Lisp]], [[Islisp|ISLISP]], [[Logo语言|Logo]], {{tsl|en|newLISP|}}, [[Racket]], [[Scheme]], {{tsl|en|Cadence SKILL|SKILL}}
| dialects = [[Arc]], [[AutoLISP]], [[Clojure]], [[Common Lisp]], [[Emacs Lisp]], [[Islisp|ISLISP]], [[Logo语言|Logo]], {{tsl|en|newLISP|}}, [[Racket]], [[Scheme]], {{tsl|en|Cadence SKILL|SKILL}}
| influenced by = [[資訊處理語言|IPL]]
| influenced by = [[資訊處理語言|IPL]]
| influenced = [[CLU]], [[Dylan (编程语言)|Dylan]], [[Falcon]], [[Forth]], [[Haskell]], [[Io (编程语言)|Io]], Ioke, [[JavaScript]], [[Lua]], [[LPC (程式语言)|LPC]], {{en-link|MDL (编程语言)|MDL (programming language)|MDL}},[[ML语言|ML]], {{tsl|en|Nu_(programming_language)|Nu语言|Nu}}, {{tsl|en|OPS5|}}, [[Perl]],[[POP-2]]/{{tsl|en|POP-11||11}}, [[Python]], [[REBOL]], [[Ruby]], [[Smalltalk]], [[Wolfram语言]]
| influenced = [[CLU]], [[Dylan (编程语言)|Dylan]], [[Falcon]], [[Forth]], [[Haskell]], [[Io (编程语言)|Io]], Ioke, [[JavaScript]], [[Julia (编程语言)|Julia]]<ref name="Julia">{{cite web |url=http://julia.readthedocs.org/en/latest/manual/introduction/ |title=Introduction |work=The Julia Manual |publisher=Read the Docs |access-date=2016-12-10 |url-status=dead |archive-url=https://web.archive.org/web/20160408134008/http://julia.readthedocs.org/en/latest/manual/introduction/ |archive-date=2016-04-08}}</ref>, [[Lua]], [[LPC (程式语言)|LPC]], {{en-link|MDL (编程语言)|MDL (programming language)|MDL}},[[ML语言|ML]], {{tsl|en|Nu_(programming_language)|Nu语言|Nu}}, {{tsl|en|OPS5|}}, [[Perl]],[[POP-2]]/{{tsl|en|POP-11||11}}, [[Python]], [[REBOL]], [[Ruby]], [[Smalltalk]], [[Wolfram语言]]<ref name="Wolfram">{{cite web |url=https://www.wolfram.com/language/faq/ |title=Wolfram Language Q&A |publisher=Wolfram Research |access-date=2016-12-10}}</ref>
}}
}}
'''Lisp'''(歷史上拼寫為'''LISP''')是具有悠久歷史的計算機編程語言家族,有獨特和完全括號的[[波兰表示法|前綴符號表示法]]。起源於西元1958年,是現今第二悠久而仍廣泛使用的高階編程語言。只有[[Fortran|FORTRAN]]編程語言比它更早一年。Lisp編程語族已經演變出許多種方言。現代最著名的通用編程語種是[[Scheme]]、[[Common Lisp]]和[[Clojure]]。
'''Lisp'''(歷史上拼寫為'''LISP''')是具有悠久歷史的計算機編程語言家族,有獨特和完全括號的[[波兰表示法|前綴符號表示法]]<ref name="6AN93">{{cite book | title = Milestones in computer science and information technology | author = Edwin D. Reilly | publisher = Greenwood Publishing Group | year = 2003 | isbn = 978-1-57356-521-9 | pages = 156–157 | url = https://books.google.com/books?id=JTYPKxug49IC&pg=PA157}}</ref>。起源於西元1958年,是現今第二悠久而仍廣泛使用的高階編程語言。只有[[Fortran|FORTRAN]]編程語言比它更早一年<ref name="uOUnJ">{{cite web|url=http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-5.html|archive-url=https://web.archive.org/web/20010727170154/http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-5.html|url-status=dead|archive-date=2001-07-27|quote=Lisp is a survivor, having been in use for about a quarter of a century. Among the active programming languages only Fortran has had a longer life.|title=SICP: Foreword}}</ref><ref name="AfFRW">{{cite web|url=http://www-formal.stanford.edu/jmc/history/lisp/node6.html#SECTION00060000000000000000|title=Conclusions|access-date=2014-06-04|archive-url=https://web.archive.org/web/20140403021353/http://www-formal.stanford.edu/jmc/history/lisp/node6.html#SECTION00060000000000000000|archive-date=2014-04-03|url-status=dead}}</ref>。Lisp編程語族已經演變出許多種方言。現代最著名的通用編程語種是[[Scheme]]、[[Common Lisp]]和[[Clojure]]。


Lisp最初創建時受到[[阿隆佐·邱奇]]的[[λ演算|lambda演算]]的影響,用來作為計算機程序實用的數學表達。因為是早期的高階編程語言之一,它很快成為人工智能研究中最受歡迎的編程語言。在計算機科學領域,Lisp開創了許多先驅概念,包括:[[树结构]]、[[垃圾回收 (計算機科學)|自動記憶體管理]]、[[类型系统|动态类型]]、[[条件表达式]]、[[高階函數]]、[[遞迴]]、自主(self-hosting)編譯器、[[讀取﹣求值﹣輸出循環]]({{lang-en|Read-Eval-Print Loop,REPL}})。
Lisp最初創建時受到[[阿隆佐·邱奇]]的[[λ演算|lambda演算]]的影響,用來作為計算機程序實用的數學表達<ref name="Steele, Guy Lewis, Jr.; Sussman, Gerald Jay">{{cite web |url=https://dspace.mit.edu/handle/1721.1/6094/ |title=The Art of the Interpreter, or the Modularity Complex (Parts Zero, One, and Two), Part Zero, P. 4 |publisher=MIT Libraries |hdl=1721.1/6094 |access-date=2020-08-01}}</ref>。因為是早期的高階編程語言之一,它很快成為人工智能研究中最受歡迎的編程語言<ref name="APRO">{{cite web |url=https://apro-software.com/top-programming-languages-in-ai/ |title=The Top Programming Languages in Artificial Intelligence |work=[[Artificial Intelligence]] |publisher=APRO |access-date=2021-02-15 |url-status=live |archive-url=https://web.archive.org/web/20201030142849/https://apro-software.com/top-programming-languages-in-ai/ |archive-date=2020-10-30}}</ref>。在計算機科學領域,Lisp開創了許多先驅概念,包括:[[树结构]]、[[垃圾回收 (計算機科學)|自動記憶體管理]]、[[类型系统|动态类型]]、[[条件表达式]]、[[高階函數]]、[[遞迴]]、自主(self-hosting)編譯器<ref name="Graham">{{cite web |title=Revenge of the Nerds |author=Paul Graham |url=http://www.paulgraham.com/icad.html |access-date=2013-03-14}}</ref>、[[讀取﹣求值﹣輸出循環]]({{lang-en|Read-Eval-Print Loop,REPL}})<ref name="hC1qm">{{Cite book|url=http://www.informit.com/articles/article.aspx?p=1671639&seqNum=3|title=Influential Programming Languages, Part 4: Lisp|last=Chisnall|first=David|date=2011-01-12}}</ref>


"LISP"名稱源自「列表處理器」({{lang-en|LISt Processor}})的縮寫。[[連結串列|列表]]是Lisp的主要數據結構之一,Lisp編程代碼也同樣由列表組成。因此,Lisp程序可以把源代碼當作數據結構進行操作,而使用其中的宏系統,開發人員可將自己定義的新語法或領域專用的語言,嵌入在Lisp編程中。
"LISP"名稱源自「列表處理器」({{lang-en|LISt Processor}})的縮寫<ref name="ArtOfLisp">{{cite book|last1=Jones|first1=Robin|last2=Maynard|first2=Clive|last3=Stewart|first3=Ian|title=The Art of Lisp Programming|date=December 6, 2012|publisher=Springer Science & Business Media|isbn=9781447117193|page=2}}</ref>。[[連結串列|列表]]是Lisp的主要數據結構之一,Lisp編程代碼也同樣由列表組成。因此,Lisp程序可以把源代碼當作數據結構進行操作,而使用其中的宏系統,開發人員可將自己定義的新語法或領域專用的語言,嵌入在Lisp編程中。


代碼和數據的可互換性為Lisp提供了立即可辨識的語法。所有的Lisp程序代碼都寫為[[S-表達式]]或以括號表示的列表。函數調用或語義形式也同樣寫成列表,首先是函數或操作符的名稱,然後接著是一或多個參數:例如,取三個參數的函數<code>f</code>即為<code>(f arg1 arg2 arg3)</code>。
代碼和數據的可互換性為Lisp提供了立即可辨識的語法。所有的Lisp程序代碼都寫為[[S-表達式]]或以括號表示的列表。函數調用或語義形式也同樣寫成列表,首先是函數或操作符的名稱,然後接著是一或多個參數:例如,取三個參數的函數<code>f</code>即為<code>(f arg1 arg2 arg3)</code>。

2021年10月24日 (日) 11:40的版本

Lisp
编程范型多范型函数式过程式反射式元编程
設計者约翰·麦卡锡
實作者史帝芬·羅素, Timothy P. Hart和Mike Levin
发行时间1958年,​66年前​(1958
型態系統动态类型强类型
衍生副語言
Arc, AutoLISP, Clojure, Common Lisp, Emacs Lisp, ISLISP, Logo, newLISP, Racket, Scheme, SKILL英语Cadence SKILL
啟發語言
IPL
影響語言
CLU, Dylan, Falcon, Forth, Haskell, Io, Ioke, JavaScript, Julia[1], Lua, LPC, MDL英语MDL (programming language),ML, Nu英语Nu_(programming_language), OPS5英语OPS5, Perl,POP-2/11英语POP-11, Python, REBOL, Ruby, Smalltalk, Wolfram语言[2]

Lisp(歷史上拼寫為LISP)是具有悠久歷史的計算機編程語言家族,有獨特和完全括號的前綴符號表示法[3]。起源於西元1958年,是現今第二悠久而仍廣泛使用的高階編程語言。只有FORTRAN編程語言比它更早一年[4][5]。Lisp編程語族已經演變出許多種方言。現代最著名的通用編程語種是SchemeCommon LispClojure

Lisp最初創建時受到阿隆佐·邱奇lambda演算的影響,用來作為計算機程序實用的數學表達[6]。因為是早期的高階編程語言之一,它很快成為人工智能研究中最受歡迎的編程語言[7]。在計算機科學領域,Lisp開創了許多先驅概念,包括:树结构自動記憶體管理动态类型条件表达式高階函數遞迴、自主(self-hosting)編譯器[8]讀取﹣求值﹣輸出循環(英語:Read-Eval-Print Loop,REPL[9]

"LISP"名稱源自「列表處理器」(英語:LISt Processor)的縮寫[10]列表是Lisp的主要數據結構之一,Lisp編程代碼也同樣由列表組成。因此,Lisp程序可以把源代碼當作數據結構進行操作,而使用其中的宏系統,開發人員可將自己定義的新語法或領域專用的語言,嵌入在Lisp編程中。

代碼和數據的可互換性為Lisp提供了立即可辨識的語法。所有的Lisp程序代碼都寫為S-表達式或以括號表示的列表。函數調用或語義形式也同樣寫成列表,首先是函數或操作符的名稱,然後接著是一或多個參數:例如,取三個參數的函數f即為(f arg1 arg2 arg3)

Lisp语言的主要现代版本包括SchemeCommon LispRacket以及Clojure。1980年代蓋伊·史提爾二世编写了Common Lisp试图进行标准化,这个标准被大多数解释器和编译器所接受。还有一种是編輯器Emacs所衍生出來的Emacs Lisp(而Emacs正是用Lisp作为扩展语言进行功能扩展)非常流行,并建立了自己的标准。

歷史

20世纪

1955年至1956年間,資訊處理語言被創造出來,用於人工智能處理(早期的基于符号处理的人工智能领域,以图灵测试为目标)。它首先使用了列表(抽象数据类型)與遞歸。

1958年,約翰·麥卡錫麻省理工學院發明了Lisp程式語言,採用了資訊處理語言的特徵。1960年,他在《ACM通讯》發表論文,名為《遞迴函數的符號表達式以及由機器運算的方式,第一部》(Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I)[11]。在這篇論文中闡述了只要透過一些簡單的運算子,以及用於函數的記號,就可以建立一個具圖靈完備性語言,可用於演算法中。

麥卡錫最初使用M-表達式寫程式碼,之後再轉成S-表達式,舉例來說M-表達式的語法,car[cons[A,B]],等同於S-表達式的(car (cons A B))。然而由於S-表達式具備同像性的特性(homoiconic,即程式與資料由同樣的結構儲存),實際應用中一般只使用S-表達式。此外他也借用了資訊處理語言中的许多概念。

約翰·麥卡錫的學生史帝芬·羅素在閱讀完此論文後,認為Lisp編程語言當中的eval函数可以用機器碼來實做。他在IBM 704機器上,寫出了第一個Lisp解释器[12]。1962年,蒂姆·哈特(Tim Hart)與麥克·萊文(Mike Levin)在麻省理工學院以Lisp編程語言,實做出第一個完整的Lisp編譯器。這兩人在筆記中使用的語法比麥卡錫早期的代碼更接近現代Lisp風格。

然而使用20世紀70年代當時的編譯器技術和硬體,要實現Lisp還是困難的挑戰。由研究生丹尼爾·愛德華茲所開發的垃圾收集程序,使得在通用計算機上運行Lisp變得實用,但效率仍然是一個問題。這導致了Lisp專用機器的創建:用於運行Lisp環境和程序的專用硬體。之後計算機硬體和編譯器技術的發展迅速,使得昂貴的Lisp專用機器過時。

联系于人工智能

自从创始以来,Lisp就密切联系于人工智能研究社群,特别是在PDP-10系统之上[13]。Lisp被用于实现编程语言Micro Planner英语Planner (programming language),它被用在著名的AI系统SHRDLU之中。在1970年代,随着AI研究催生了商业分支,存有的Lisp系统的性能成为日益严重的问题。

谱系和方言

在它六十年的历史中,Lisp产生了在S-表达式语言的核心主旨上的很多变体。此外,每个给定方言有可能有多种实现,例如Common Lisp就有十余种以上的实现。

在方言间的差异可以是非常显眼的,例如Common Lisp使用关键字defun来定义一个函数,而Scheme使用define[14]。在一种标准化了的方言之内,符合标准的实现支持相同的核心语言,但是有着不同的扩展和函数库。

历史上的重要方言

Lisp机器,现存于MIT博物馆英语MIT Museum

2000年迄今

在20世紀90年代衰退之後,Lisp最近十年來因一些關注而逐漸復甦。大多數新活動都集中在SchemeCommon LispEmacs LispRacketClojure的實作上,包括開發新的跨平台函式庫和應用。當其他人認為Lisp已經是過時陳舊的,如保羅·格雷厄姆埃里克·雷蒙等人繼續出版有關於Lisp編程的著作,一些新的開發人員受到這些作者啟發,經常將Lisp這種語言描述為令人大開眼界的經驗,並聲稱在本質上比較其它編程語言更有生產效率。這種意識的提高可對比於,如同Lisp在90年代中期“人工智能冬季”的短暫增長。

Dan Weinreb在他的調查中,列出了11個積極維護中的Common Lisp實作。Scieneer Common Lisp是一個新的實作商業化產品,由CMUCL於2002年首次發布。

開源社群建立了新的支援基礎:CLiki是個收集Common Lisp相關資訊的維基,Common Lisp目錄列出了資源,#lisp是一個受歡迎的IRC頻道,可以共享和註釋代碼片段(在lisppaste的支持下,一個用Lisp編寫的IRC機器人),Planet Lisp收集了各種 Lisp相關博客的內容,LispForum用戶討論Lisp主題,Lispjobs是個公布職缺機會的服務,還有一個Weekly Lisp News提供每週新聞。Common-lisp.net是開源專案的託管站點。Quicklisp則是含括了許多函式庫的裝載管理器。

Lisp50@OOPSLA慶祝了Lisp的50週年(1958-2008)。在波士頓,溫哥華和漢堡有定期的當地用戶會議。其他活動包括歐洲共同Lisp會議,歐洲Lisp專題討論會和國際Lisp研討會。Scheme社群積極維護了二十多個實作。在過去幾年中已開發了數個有意義的新實作(Chicken,Gambit,Gauche,Ikarus,Larceny,Ypsilon),Scheme社群廣泛接納了R5RS語言標準。Scheme需求實作過程建立了很多預備標準函式庫和Scheme擴展功能。各種 Scheme實作的用戶社群持續地增長。

一個新的語言標準化過程於2003年開始,並在2007年產生了R6RS標準,而使用Scheme介紹計算機科學課程的學校似乎有所減少。麻省理工學院的計算機科學入門課程,已經不再使用Scheme。

現今有幾種新的Lisp方言:ArcHyNu英语Nu (programming language)、Liskell和LFE英语LFE (programming language)

Lisp編程語族時間軸

主要方言

Common LispScheme代表了Lisp发展的两个主流。这些语言体现了显著不同的设计选择。

Common LispMaclisp的后继者。对它有重要影响的是Lisp Machine Lisp英语Lisp Machine Lisp、Maclisp、NIL英语NIL (programming language)S-1 Lisp英语S-1 LispSpice Lisp英语Spice Lisp和Scheme[26]。它拥有Lisp Machine Lisp(用于编程Lisp机器的大型Lisp方言)的很多特征,但设计上能高效的实现在任何个人计算机或工作站上。Common Lisp是通用编程语言,因而拥有一个大型语言标准,包括很多内建数据类型、函数、宏和其他语言元素,以及一个对象系统(Common Lisp对象系统)。Common Lisp还从Scheme借鉴了特定特征,比如词法作用域词法闭包。Common Lisp实现目标定为不同的平台,比如:LLVM[27]Java虚拟机[28]、x86-64、PowerPC、Alpha、ARM、Motorola 68000和MIPS[29],和不同的操作系统,比如:Windows、macOS、Linux、Solaris、FreeBSD、NetBSD、OpenBSD、Dragonfly BSD和Heroku[30]

Scheme是一个静态作用域和真正尾递归的Lisp编程语言方言,由Guy L. Steele, Jr.Gerald Jay Sussman发明。它的设计有着异常清晰和简单的语义,和很少的形成表达式的不同方式。它的设计大约比Common Lisp早上一个年代,Scheme是一个相当极简主义的设计。它拥有非常小的标准特征集合,但具有在Common Lisp中未规定的特定实现特征(比如尾递归优化和完全续体)。在Scheme中能方便的表达广阔的编程范型,包括指令式、函数式和消息传递风格。Scheme通过一系列的标准(算法语言Scheme修订n报告)和一系列Scheme实现要求英语Scheme Requests for Implementation而持续的演化。

Clojure是Lisp的一个新近方言,其目标主要是Java虚拟机通用语言运行库(CLR)、Python VM、Ruby VM YARV和编译成JavaScript。它被设计为一个务实的通用编程语言。Clojure受到了Haskell的相当大的影响,因而非常强烈的强调了不可变性[31]。Clojure提供了对Java框架和库的访问,具有可选的类型提示和类型推论,这样到Java的调用就可以避免反射并确使了快速的原始操作。Clojure设计上不后向兼容于其他Lisp方言[32]

进一步的,Lisp方言在很多应用中被用作脚本语言,其中最周知的是在Emacs编辑器中的Emacs Lisp,在AutoCAD中的AutoLISP和后来的Visual Lisp, Audacity中的Nyquist,和LilyPond中的Scheme。有用的Scheme解释器潜在的有更小大小,使得它特别流行于嵌入式脚本。例子包括SIODTinyScheme,二者都曾经以通用名字“Script-fu”成功的嵌入到了GIMP图像处理软件中[33]。LIBREP是John Harper最初基于Emacs Lisp语言开发的Lisp解释器,它已经嵌入到了Sawfish窗口管理器[34]

标准化的方言

Lisp有官方标准化的方言:R6RS SchemeR7RS Scheme、IEEE Scheme[35]ANSI Common Lisp和ISO ISLISP

语法和语义

注意:本文的例子是用Common Lisp书写(但是其多数在Scheme中也是有效的)。

符号表达式(S-表达式)

Lisp是一个面向表达式编程语言英语Expression-oriented programming language。不同于多数其他语言,在表达式和语句之间不做区分。所有代码和数据都写为表达式。当求值一个表达式的时候,它产生一个值(在Common Lisp中可能有多个值),它可以接着被嵌入到其他表达式中。每个值都可以是任何数据类型的。

McCarthy的1958年论文介入两种类型的语法:符号表达式(S-表达式或sexps),它镜像了代码和数据的内部表示;和元表达式(M-表达式英语M-expression),它表达S-表达式的函数。M-表达式从未得到青睐,几乎所有今天的Lisp都使用S-表达式来操纵代码和数据二者。

圆括号的使用是Lisp与其他编程语言家族最直接明显的差别。为此学生们一直将Lisp昵称为“迷失在愚蠢的括号中”(Lost In Stupid Parentheses)或“大量烦人的多余括号”(Lots of Irritating Superfluous Parentheses)[36]。但是S-表达式语法也承担了Lisp多数能力:语法极其正规,利于计算机操纵。然而Lisp的语法不局限于传统的括号表示法。它可以扩展为包括替代表示法。例如,XMLisp是Common Lisp扩展,它采用元对象协议来集成S-表达式和可扩展标记语言(XML)。

对表达式的依赖给与语言巨大的灵活性。由于Lisp函数都写为列表,它们可以完全就像数据那样处理。这允许了轻易的书写能操纵其他程序的程序(元编程)。很多Lisp方言使用鸿系统来利用这个特征,它确使了语言扩展而几乎没有限制。

列表

书写Lisp列表是以空白来分隔其元素的,并包围以圆括号。例如,(1 2 foo)是其元素为三个原子12foo的一个列表。这些值是隐含的有类型的:它们分别是两个整数和叫做“符号”的Lisp专有数据类型,但不需要显式的声明。

空列表()也表示为特殊原子nil。这是Lisp中既是原子又是列表的唯一实体。

表示式被写为使用前缀表示法的列表。在列表中第一个元素是一个函数的名字、一个宏的名字、一个lambda表达式或特殊算符的名字(见后)。列表的余下部份是实际参数。例如,函数list将它的实际参数作为一个列表返回,所以表达式:

 (list 1 2 (quote foo))

求值为列表(1 2 foo)。在前面例子中的foo之前的quote是特殊算符,它不求值就返回它的实际参数。任何未引述的表达式,在外围表达式被求值之前,都递归的被求值。例如:

 (list 1 2 (list 3 4))

求值为列表(1 2 (3 4))。注意第三个实际参数是一个列表;列表是可以嵌套的。

算符

对算术算符的对待都是类似的。表达式:

 (+ 1 2 3 4)

求值为10。其在中缀表示法下的等价形式为"1 + 2 + 3 + 4"。

Lisp没有在Algol派生语言中实现的那样的算符概念。在Lisp中算术算符是可变参数函数(或多元函数),能够接受任何数目的实际参数。C-风格的++增加算符,有时在名字incf之下实现,给出的语法是:

 (incf x)

等价于(setq x (+ x 1)),它返回x的新值。

特殊算符(有时叫做特殊形式)提供了Lisp的控制结构。例如,特殊算符if接受三个实际参数。如果第一个实际参数为非nil,它求值为第二实际参数;否则它求值为第三个实际参数。因此表达式:

 (if nil
     (list 1 2 "foo")
     (list 3 4 "bar"))

求值为(3 4 "bar")。当然,如果在nil的位置替换上非平凡的表达式会更有用处。

Lisp还提供逻辑算符andornotandor算符进行短路求值,并分别返回它们的第一个nil或非nil实际参数:

 (or (and "zero" nil "never") "James" 'task 'time)

会求值为"James"

Lambda表达式和函数定义

另一个特殊算符lambda,被用来绑定变量到值,接着对表达式求进行求值。这个算符还被用于建立函数:给lambda的实际参数是一个形式参数列表,和这个函数要求值的一个或多个表达式,它的返回值是其求值的最后一个表达式的值。表达式:

 (lambda (arg) (+ arg 1))

求值为一个函数,它接受一个实际参数,绑定它到arg并返回比这个实际参数大一的数。对待Lambda表达式于命名函数没有区别;它们以相同方式调用。因此,表达式:

 ((lambda (arg) (+ arg 1)) 5)

求值为6。这里我们做了一次函数应用:我们通过传递给它值5而执行了这个匿名函数

命名函数是通过使用defun宏将一个lambda表达式存储在一个符号之中而建立的:

 (defun foo (a b c d) (+ a b c d))

(defun f (a) b...)在全局环境中定义一个名为f的新函数。它在概念上类似于表达式:

 (setf (fdefinition 'f) #'(lambda (a) (block f b...)))

这里的setf是一个宏,用来设置第一个实际参数fdefinition 'f为一个新的函数对象。fdefinition是给名为f的函数的全局函数定义。#'是特殊算符function的简写,它返回一个函数对象。

原子

在最初的LISP中,有两种基础数据类型:原子和列表。列表是元素的一个有限有序序列,这里的每个元素要么是一个原子要么是一个列表,而原子要么是一个要么是一个符号。符号实质上是唯一性命名的项目,在源代码中写为字母数字串,并被要么用作一个变量名字要么符号处理英语Computer algebra中的一个数据项目。例如,列表(FOO (BAR 1) 2)包含三个元素:符号FOO、列表(BAR 1)和数2

在原子和列表之间的本质区别是原子是不可变的和唯一性的。出现在源代码中不同位置的两个原子,如果以完全相同方式写成则表示相同的对象,而每个列表都是一个分立的对象,它们可以独立于其他列表而改变 ,并可以通过比较算符而区分于其他列表。

随着后来的Lisp介入了更多的数据类型,和编程风格的演化,原子的概念失去了重要性。很多方言出于遗产兼容性而保留了谓词atom,定义它为对不是cons的任何对象都为真。

cons和列表

list (42 69 613)的方框与指针示意图

Lisp列表被实现为单向链表[37]。这个链表的每个单元都叫做cons(在Scheme中叫做pair),它构成自两个指针,分别叫做carcdr

在众多可以用cons单元构建的数据结构中,最基本一个叫做“真正列表”(proper list)。真正列表要么是特殊的nil(空列表)符号,要么是一个cons,它的car指向一个数据项(它可以是另一个cons结构比如一个列表),而cdr指向另一个真正列表。

如果一个给定cons被接受为一个链表的头部,那么它的car指向这个列表的第一个元素,而它的cdr指向这个列表的余下部份。为此,在提及作为链表(而非树等)一部份的cons的时候,carcdr函数也分别叫做firstrest

因此,Lisp列表不是原子对象,它们类似C++或Java中的容器类的实例。一个列表就是链接的cons的一个聚集。引用一个给定列表的变量,简单的就是到列表中第一个cons的指针。遍历一个列表可以通过逐一cdr这个列表来进行,就是说连续的选取cdr来访问这个列表的每个cons;或者通过使用任何一个将一个函数映射在一个列表之上的高阶函数

由于cons和列表在Lisp系统中是普遍性的,经常有人误解它们是Lisp的唯一数据结构。事实上,除了最简单者之外的所有Lisp都有其他数据结构,比如向量(数组英语Array data type)、散列表、结构等等。

S-表达式表示列表

圆括号的S-表达式表示了链表结构。有多种方式将相同的列表表示为一个S-表达式。cons可以用“点对表示法”写为(a . b),这里的acarbcdr。更长的真正列表可以用点对表示法写为(a . (b . (c . (d . nil))))。这通常简写为列表表示法的(a b c d)。一个非真正列表[38],可以用二者的组合来书写,比如列表(a b c . d)有三个cons,最后的cdrd(也就是完全特殊形式下的(a . (b . (c . d))))。

列表处理过程

Lisp提供很多内建的过程,用来访问和控制列表。列表可以直接用list过程创建,它接受任何数目的实际参数,并返回这些实际参数的列表:

 (list 1 2 'a 3)
 ;Output: (1 2 a 3)
 (list 1 '(2 3) 4)
 ;Output: (1 (2 3) 4)

由于列表是从cons对构造而来,可以使用cons过程来在一个列表的前端增加一个元素。注意由于列表构造方法,cons在处理列表实际参数上是不对称的:

 (cons 1 '(2 3))
 ;Output: (1 2 3)
 (cons '(1 2) '(3 4))
 ;Output: ((1 2) 3 4)

append过程将两个(或更多)列表相互附加。由于Lisp列表是链表,附加两个列表有渐进时间复杂度

 (append '(1 2) '(3 4))
 ;Output: (1 2 3 4)
 (append '(1 2 3) '() '(a) '(5 6))
 ;Output: (1 2 3 a 5 6)

共享结构

Lisp列表,作为单向链表,可以相互共享结构。就是说,两个列表可以有相同的尾部,或者最终的cons序列。例如,在执行下列Common Lisp代码之后:

(setf foo (list 'a 'b 'c))
(setf bar (cons 'x (cdr foo)))

列表foobar分别是(a b c)(x b c)。然而,尾部(b c)在两个列表中都是相同的结构。它不是复件;对于两个列表指向bccons单元都在相同的内存位置。

共享结构而非复制可以得到相当客观的性能提升。但是这种技术可能以未预期的方式,交互于改变作为实际参数传递给它的列表的那些函数。改变一个列表,比如替代cgoose,将会影响另一个列表:

 (setf (third foo) 'goose)

这变更foo(a b goose),但是从而还变更了bar(x b goose),这是可能是未预期的结果。这是缺陷的来源,为此改变它们的实际参数的那些函数被文档标示为破坏性(destructive)的。

函数式编程爱好者避免破坏性函数。在青睐函数式风格的Scheme方言中,破坏性函数的名字都标记了警告性感叹号,或者叫做“bang”,比如set-car!(读作set car bang),它替换一个conscar。在Common Lisp方言中,破坏性函数是司空见惯的,与set-car!等价的是rplaca,它的名字表示“replace car”。这个函数是不常见的,因为Common Lisp包括了一个特殊设施setf,用来轻易的定义和使用破坏性函数。在Common Lisp中常见的风格是在构建原型的时候写函数式代码(没有破坏性调用),接着将破坏性调用作为优化增加于可以安全的进行它们的地方。

自求值形式和引述

Lisp求值用户录入的表达式。符号和列表求值为某个其他(通常更简单的)表达式,例如:一个符号求值为它指名的变量的值;(+ 2 3)求值为5。但是,多数其他形式求值为其自身:如果录入5到Lisp中,它返回5

任何表达式都可以标记上防止被求值(对于符号和列表是需要的)。这是特殊算符quote的角色,它也简写为'(一个单引号)。例如,通常如果录入了符号foo,它返回对应变量的值(没有这个变量则为一个错误)。要引用这个文字英语Literal (computer programming)符号,录入要么(quote foo) ,要么更常见的'foo

Common Lisp和Scheme二者还支持“反引述”(backquote)算符(在Scheme中叫做准引述英语quasiquote),这时录入`字符(重音符)。它几乎同于普通引述,除了它允许表达式被求值,并将它们的值插入到引述列表之中,这些表达式标记了逗号,表示去引述,或逗号-at,@表示拼接算符。如果变量snue有值(bar baz),则`(foo ,snue)求值为(foo (bar baz)),而`(foo ,@snue)求值为(foo bar baz)。反引述经常用于定义宏展开[39][40]

自求值形式和引述形式是Lisp中文字英语Literal (computer programming)的等价者。在程序代码中可以修改(可变)文字的值。例如,如果一个函数返回一个引述形式,而调用这个函数的代码修改这个形式,这可以改变这个函数在后续调用时的行为:

(defun should-be-constant ()
  '(one two three))

(let ((stuff (should-be-constant)))
  (setf (third stuff) 'bizarre))   ; bad!

(should-be-constant)   ; returns (one two bizarre)

像这样修改一个引述形式通常被认为是不良风格,并且被ANSI Common Lisp定义为是危险的。它会导致在编译文件中的未定义的行为,因为文件编译器可以合并类似的常量并将它们放置到写保护内存中,等等。

Lisp的引述形式化已经被Douglas Hofstadter(在《Gödel, Escher, Bach》中)和其他人注解为自引用哲学想法的例子。

作用域和闭包

Lisp家族分裂为使用动态或使用静态(也叫做词法)作用域ClojureCommon LispScheme缺省使用静态作用域,而newLISPPicolisp和嵌入EmacsAutoCAD的语言使用动态作用域。自从版本24.1,Emacs使用动态和词法作用域二者。

程序代码的列表结构

在Lisp和其他语言之间的基本区别是,在Lisp中一个程序的文本表示,简单的是人类可读的描述,它同于底层Lisp系统使用的内部数据结构(链表、符号、数、字符等)。

Lisp利用这一点来实现了一个非常强力的宏系统。就像其他宏语言比如C,一个宏返回可以接着被编译的代码。但是不同于C的宏,Lisp的宏是函数因而可以利用Lisp的全部能力。

进一步的,因为Lisp代码作为列表有着相同的结构,宏可以使用语言中任何列表处理函数来建造。简要的说,Lisp可以在数据结构上做的任何事情,Lisp宏都可以在代码上做。相反的,在多数其他语言中,解析器的输出是纯粹的内部于语言实现的而不能被编程者操纵。

这个特征可以在语言中开发高效语言。例如,Common Lisp对象系统可以使用宏清晰的实现为一个语言扩展。这意味着如果一个应用需要不同的继承机制,它可以使用不同的对象系统。这直接的对立于多数其他语言;例如,Java不能支持多重继承并且没有增加它的合理方式。

在简单的Lisp实现中,这个列表结构被直接的解释来运行这个程序;一个函数是在文字上的一段列表结构,它被解释器在执行它的时候遍历。但是,多数后来的Lisp系统还包括一个编译器。编译器将列表结构转换成机器代码或字节码用于执行。这个代码可以像用常规语言比如C编译的代码一样快速。

宏在编译步骤之前展开,因而提供一些有价值的选项。如果一个程序需要一个预先计算了的表格,那么一个宏可以在编译时间建立这个表格,所以编译器只需要输出这个表格,而不需要在运行时间调用代码来建立这个表格。一些Lisp实现甚至拥有一种eval-when机制,允许代码在编译时间期间出现(在一个宏需要它的时候),而不出现在发行的模块中[41]

求值和读取–求值–打印循环

Lisp语言经常被以交互式命令行来使用,它还可以结合入集成开发环境(IDE)。用户在命令行录入表达式,或指示IDE将它们传送给Lisp系统。Lisp读取录入的表达式,求值它们,并打印结果。为此,Lisp命令行被叫做读取﹣求值﹣输出循环(REPL)。

REPL的基本操作描述如下。这是一个简化的描述,省略了很多真实Lisp的元素,比如引述和宏。

read函数接受文本的S-表达式作为输入,并将它们解析为内部数据结构。例如,如果你在提示符下录入文本(+ 1 2)read将它转换成有三个元素的一个链表:符号+、数1和数2。恰巧这个列表也是一段有效的Lisp代码,就是说它是可以被求值的。这是因为car这个列表指名了一个函数即加法运算。

注意foo将被读作一个单一符号。123将被读作数一百二十三。"123"将被读作字符串"123"

eval函数求值数据,返回零或多个其他Lisp数据作为结果。求值不必然意味着解释;一些Lisp系统编译所有的表达式为机器代码。但将求值描述为解释是很简单的:要求值其car指名一个函数的一个列表,eval首先求值在它的cdr中的每个实际参数,接着应用这个函数于这些实际参数。在这个案例中,这个函数是加法,而应用它于实际参数列表(1 2)产生答案3。这是这个求值的结果。

符号foo求值为符号foo的值。数据比如字符串"123"求值为相同的字符串。列表(quote (1 2 3))求值为列表(1 2 3)

print函数的工作是将输入表示给用户。对于简单结果比如3这是平凡的。求值为一段列表结构的一个表达式会要求print遍历这个列表并将结果输出为一个S-表达式。

要实现一个Lisp REPL,必需的只是实现这三个函数和一个无限循环函数。实现eval函数会很复杂是自然的,因为它必须也要实现所有特殊算符比如iflambda。它们完成后,一个基本的REPL就是一行代码:(loop (print (eval (read))))

Lisp REPL典型的也提供输入编辑、输入历史、错误处理和到调试器的接口。

Lisp通常使用及早求值。在Common Lisp中,实际参数以应用式次序(最左最内为先)求值,而在Scheme中实际参数的次序是未定义的,为编译器优化留下了余地。

控制结构

Lisp最初有很少的控制结构,但是在语言演化期间却增加了很多。Lisp的最初条件算符cond,是后来的if-then-else结构的先驱。

Scheme方言的编程者经常使用尾递归表达循环。Scheme在学术计算机科学中的通行性,导致了一些学生相信尾递归是在Lisp中书写迭代的唯一的或最常用的方式,但是这是不正确的。所有常见的Lisp方言都有指令式风格的迭代构造,从Scheme的do循环到Common Lisp的复杂的loop表达式。此外,使得这成为客观上而非主观上的一件事的关键要点,是Scheme对尾递归的处理提出了特殊要求,Scheme通常鼓励使用尾递归的原因,是语言定义明确的支持了这种实践。与之相反,ANSI Common Lisp不要求常称为尾递归消除的这种优化[42]。因此,不鼓励将尾递归风格作为使用更传统的迭代构造(比如dodolistloop)的替代品[43]。在Common Lisp中不只是风格偏好的问题,而是潜在的效率问题,因为在Common Lisp中明显的尾递归可能未被编译为简单的jump;和程序正确性问题,因为在Common Lisp中尾递归可能增加栈的使用而有堆栈溢出风险。

一些Lisp控制构造是特殊算符,等价于其他语言的语法关键字。使用这些算符的表达式与函数调用有着相同的表面外观,但是不同之处在于参数不是必须求值的,或者在迭代表达式的情况下,可以被求值多于一次。

对比于多数其他主要编程语言,Lisp允许使用语言自身实现控制结构。一些控制结构被实现为Lisp宏,想知道它们是如何工作的编程者甚至可以通过宏展开来研究。

Common Lisp和Scheme二者都有非局部控制流程算符。在这些算符中的不同是在这两种方言之间最深的差异。Scheme使用call/cc过程支持可重入的续体,它允许一个程序保存(并在将来恢复)执行中的特定位置。Common Lisp不支持可重入的续体,但是支持处理逃脱续体的一些方式。

相同的算法在Lisp中经常可以用要么指令式要么函数式风格来表达。如上所述,Scheme趋于青睐函数式风格,使用尾递归和续体来表达控制流程。但是,指令式风格仍是很有可能的。很多Common Lisp编程者偏好的风格,可能让使用结构化编程语言比如C的编程者看着更加熟悉,而Scheme编程者偏好的风格更加密切类似于纯函数式编程语言比如Haskell

由于Lisp在列表处理上的早期遗产,它拥有与在序列上迭代有关的一组广泛的高阶函数。在其他语言中需要显式循环(比如C中的for循环)的很多情况下,在Lisp和很多函数式编程语言中,相同的任务可以通过高阶函数来完成。

一个好的例子是在Scheme中叫做map而在Common Lisp中叫做mapcar的函数。给定一个函数和一个或多个列表,mapcar依次这个列表的元素之上连续的应用这个函数,并将结果收集入一个新的列表:

 (mapcar #'+ '(1 2 3 4 5) '(10 20 30 40 50))

这个mapcar函数将+函数应用于每个对应的元素对之上,产生结果(11 22 33 44 55)

Lisp編程語族基本Hello World範例

Scheme

(display "Hello, world!")
;; 在屏幕中打印出:Hello, world!

;; 函数定义
(define (hello)
  (display "Hello, world!"))
 
;; 函数调用
(hello)
;; 在屏幕中打印出:Hello, world!

Common Lisp

(format t "hello, world!")
;; 在屏幕中打印出:hello, world!

;; 函数定义:
(defun hello-world ()
  (format t "hello, world!"))

;; 调用函数:
(hello-world)
;; 在屏幕中打印出:hello, world!
;; 并以NIL作为函数的返回值

Clojure

(print "hello, world!")
;; 在屏幕中打印出:hello, world!

;; 函数定义:
(defn hello-world []
  (print "hello, world!"))

;; 调用函数:
(hello-world)
;; 在屏幕中打印出:hello, world!
;; 并以nil作为函数的返回值

Lisp 1.5的7个公理

quote

(quote x)返回x,我们简记为'x

(quote a)

上面的列表達式的值是a。如果使用C语言或者Java語言的列表達方式,可以說成:上面這段代碼返回的值是a'a這個列表達式和上面的那個相同,值也是a。將quote寫成'只是一種語法糖

quote起來的單一個元素會成為符號(symbol,例如'a)。符號是Lisp中的一個特別概念,他在程式碼中看起來是個字串,但並不盡然,因為符號其實會被Lisp解释器直接指向某個記憶體位置,所以當你比較'apple'apple兩個符號是否相同時,不需要像字串一樣一個個字元逐字比較,而是直接比較記憶體位置,故速度較快(使用eq運算子來比較,如果使用equal運算子會變成逐字比較)。當你定義一個函数,或者定義一個变量時,他們的內容其實就是指向一個符號。

atom

(atom x)x是一个atom或者空的list时返回原子t,否则返回NIL。在Common Lisp中我们习惯用原子t列表示真,而用空列表()NIL列表示假。

> (atom 'a)
t
> (atom '(a b c))
NIL
> (atom '())
t

现在我们有了第一个需要求出实际参数值的算符,让我们来看看quote算符的作用——通过引述一个列表,我们避免它被求值(eval)。一个未被引用的列表达式作为实际参数,atom将其视为代码,例如:

> (atom (atom 'a))
t

这是因为(atom 'a)的结果t被求出,并代入(atom (atom 'a)),成为(atom t),而这个列表达式的结果是t

反之一个被引用的列表仅仅被视为列表

> (atom '(atom 'a))
NIL

引用看上去有些奇怪,因为你很难在其它语言中找到类似的概念,但正是这一特征构成了Lisp最为与众不同的特点:代码和数据使用相同的结构来列表示,只用quote来区分它们。

eq

(eq x y)xy指向相同的对象的时候返回t,否则返回NIL,值得注意的是在Common Lisp中,原子对象在内存中只会有一份拷贝,所以(eq 'a 'a)返回t,例如:

>(eq 'a 'a)
t
>(eq 'a 'b)
NIL
> (eq '() '())
t
> (eq '(a b c) '(a b c))
NIL

car

(car x)要求x是一个列表,它返回x中的第一个元素,例如:

> (car '(a b))
a

cdr

(cdr x)同样要求x是一个列表,它返回x中除第一个元素之外的所有元素组成的列表,例如:

> (cdr '(a b c))
(b c)

cons

(cons x y)返回一个cons cell(x y),如果y不是一个list,将会以dotted pair形式展现这个cons cell,例如:

>(cons 'a 'b)
(a . b)

一个cons cell的第二项如果是另一个cons cell,就列表示成列表的形式,例如:

 (cons 'a (cons 'b 'c))

就列表示成 (a b . c) 若一个cons cell第二项为空,就省略不写,例如:

 (cons 'a  (cons 'b ()))

列表示为(a b)这样,多重的cons cell就构成了列表:

> (cons 'a (cons 'b (cons 'c ())))
(a b c)

cond

(cond (p1 e1) ...(pn en))的求值规则如下:对“条件列表达式p”依次求值,直到有一个返回t。如果能找到这样的p列表达式,相应的“结果列表达式e”的值,作为整个cond列表达式的返回值。

> (cond ((eq 'a 'b) 'first)  ((atom 'a)  'second))
 second

Lisp的巨集

Lisp的語法結構使数据与程序只是一線之隔(有quote就是数据,沒quote就是程序)。由于Lisp這種「数据即程序、程序即数据」的概念,使Lisp的巨集(Macro)變得非常有彈性:你可以定義巨集,指定它應該被編譯器翻譯(巨集展開)成什麼程式,程序和数据都可以靈活地互相轉換,最後展開的代码會成為整個程序的一部分。巨集的实现非常倚重quote來達成。當你定義了一個巨集,巨集被quote的部份會先被編譯器unquote,而沒有quote、或已經被unquote的部份,則會先被求值。最终編譯器生成的整個程序代码才是最後執行時的代码。以下以廣泛使用的Emacs Lisp為範例(以下範例亦相容Common Lisp),解釋最基本的Lisp巨集。

想要建立一個list並賦予給fruit這個变量時不能這樣做,因為這個list沒有被quote過,會被編譯器視為「程序」執行(會把"apple"這個字符串當成函数解釋),而不是「数据」,因而產生錯誤:

> (setq fruit ("apple" "banana" "citrus"))
錯誤"apple"不是一個有效函數

但這樣就正確了:

> (setq fruit (quote ("apple" "banana" "citrus")))
("apple" "banana" "citrus")
;;或者
> (setq fruit '("apple" "banana" "citrus"))
("apple" "banana" "citrus")
;;也可以用(list...)運算子,這樣一樣可以建立list。因為list本身是個函數,本來就應該被當成程式執行而不是資料,所以不會報錯:
> (setq fruit (list "apple" "banana" "citrus"))
("apple" "banana" "citrus")

前面有提到使用'符號這個語法糖能夠代替quote,但還有另外一種符號是`,意義基本上與'相同,但被`包起來的部份可以再用來unquote;而'沒有這種能力。

也就是說被`給quote起來的部份是資料,但使用逗号“,”來unquote,令被quote的数据變回程序。(注意quote只有一個arg,所以arg要用list包起來)

;;使用`來quote整個list
> `("apple" "banana" "citrus")
("apple" "banana" "citrus")

;;使用逗號,來unquote,這樣fruit這個變量會被重新求值。
> `("apple" "banana" "citrus" ,fruit)
("apple" "banana" "citrus" ("apple" "banana" "citrus"))

;;可以利用unquote的特性,定義一個函数,讓該函数根據輸入的參數回傳一個我們想要的list数据結構:
(defun user-profile (name email mobile)
  `((name . ,name)
    (email . ,email)
    (mobile . ,mobile)))

(user-profile "Richard" "rms@gnu.org" "Noooo!")
=> ((name . "Richard")
    (email . "rms@gnu.org")
    (mobile . "Noooo!"))

簡易巨集範例

這裡定義一個巨集叫做nonsense,這個巨集可以方便地定義更多以nonsense為開頭的新函數:

(defmacro nonsense (function-name)
  `(defun ,(intern (concat "nonsense-" function-name)) (input) ;intern可以將string轉成symbol
     (print (concat ,function-name input))))
;;解釋:
;;這個巨集在編譯時,`(defun  因為被quote所以不會被求值,
;;但裡面的,(intern ...)這一段從逗號開始,整個括號括起來的
;; s-expression部份會被求值。這時作為argument輸入的function-name
;;就是在這時被插進macro中。其餘剩下的部份因為仍然在`(defun的quote
;;影響之下,並不會被求值。
;;現在巨集展開完了,整個巨集才被當成一般function執行。
 
(nonsense "apple")  ;使用我們剛剛定義的nonsense這個macro來定義新的f函数
=> nonsense-apple  ;成功定義出了新的函数叫做nonsense-apple

(nonsense "banana")  ;再使用一次巨集來定義新的函数叫做nonsense-banana
=> nonsense-banana  ;成功定義了新的函数。

(nonsense-apple " is good")  ;使用剛剛定義出的新函数
=> "apple is good"
(nonsense-banana " I love to eat")  ;使用另一個剛剛定義函数
=> "banana I love to eat"

参见

参考文献

  1. ^ Introduction. The Julia Manual. Read the Docs. [2016-12-10]. (原始内容存档于2016-04-08). 
  2. ^ Wolfram Language Q&A. Wolfram Research. [2016-12-10]. 
  3. ^ Edwin D. Reilly. Milestones in computer science and information technology. Greenwood Publishing Group. 2003: 156–157. ISBN 978-1-57356-521-9. 
  4. ^ SICP: Foreword. (原始内容存档于2001-07-27). Lisp is a survivor, having been in use for about a quarter of a century. Among the active programming languages only Fortran has had a longer life. 
  5. ^ Conclusions. [2014-06-04]. (原始内容存档于2014-04-03). 
  6. ^ The Art of the Interpreter, or the Modularity Complex (Parts Zero, One, and Two), Part Zero, P. 4. MIT Libraries. [2020-08-01]. hdl:1721.1/6094. 
  7. ^ The Top Programming Languages in Artificial Intelligence. Artificial Intelligence. APRO. [2021-02-15]. (原始内容存档于2020-10-30). 
  8. ^ Paul Graham. Revenge of the Nerds. [2013-03-14]. 
  9. ^ Chisnall, David. Influential Programming Languages, Part 4: Lisp. 2011-01-12. 
  10. ^ Jones, Robin; Maynard, Clive; Stewart, Ian. The Art of Lisp Programming. Springer Science & Business Media. December 6, 2012: 2. ISBN 9781447117193. 
  11. ^ John McCarthy. Recursive functions of symbolic expressions and their computation by machine, Part I. (PDF). Communications of the ACM (ACM New York, NY, USA). 1960, 3 (4): 184–195. doi:10.1145/367177.367199. 
  12. ^ 12.0 12.1 McCarthy, J.; Brayton, R.; Edwards, D.; Fox, P.; Hodes, L.; Luckham, D.; Maling, K.; Park, D.; Russell, S. LISP I Programmers Manual (PDF). Boston, Massachusetts: Artificial Intelligence Group, M.I.T. Computation Center and Research Laboratory. March 1960. 
  13. ^ The 36-bit word size of the PDP-6/PDP-10 was influenced by the usefulness of having two Lisp 18-bit pointers in a single word. Peter J. Hurley. The History of TOPS or Life in the Fast ACs. Newsgroupalt.folklore.computers. 18 October 1990. Usenet: 84950@tut.cis.ohio-state.edu. The PDP-6 project started in early 1963, as a 24-bit machine. It grew to 36 bits for LISP, a design goal. 
  14. ^ Common Lisp: (defun f (x) x)
    Scheme: (define f (lambda (x) x)) or (define (f x) x)
  15. ^ McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I. LISP 1.5 Programmer's Manual 2nd. MIT Press. 1985 [1962]. ISBN 0-262-13011-4. 
  16. ^ Quam, Lynn H.; Diffle, Whitfield. Stanford LISP 1.6 Manual (PDF). 
  17. ^ Maclisp Reference Manual. March 3, 1979. (原始内容存档于2007-12-14). 
  18. ^ Teitelman, Warren. InterLisp Reference Manual (PDF). 1974 [2006-08-19]. (原始内容 (PDF)存档于2006-06-02). 
  19. ^ Outils de generation d’interfaces : etat de l’art et classification by H. El Mrabet
  20. ^ ftp://publications.ai.mit.edu/ai-publications/pdf/AIM-349.pdf[永久失效連結]
  21. ^ Steele, Guy L., Jr. Purpose. Common Lisp the Language 2nd. 1990. ISBN 0-13-152414-3. 
  22. ^ Kantrowitz, Mark; Margolin, Barry. History: Where did Lisp come from?. FAQ: Lisp Frequently Asked Questions 2/7. 20 February 1996. 
  23. ^ ISO/IEC 13816:1997. Iso.org. 2007-10-01 [2013-11-15]. 
  24. ^ ISO/IEC 13816:2007. Iso.org. 2013-10-30 [2013-11-15]. 
  25. ^ X3J13 Charter. 
  26. ^ Chapter 1.1.2, History, ANSI CL Standard
  27. ^ [1] Clasp is a Common Lisp implementation that interoperates with C++ and uses LLVM for just-in-time compilation (JIT) to native code.
  28. ^ [2] "Armed Bear Common Lisp (ABCL) is a full implementation of the Common Lisp language featuring both an interpreter and a compiler, running in the JVM"
  29. ^ [3] 互联网档案馆存檔,存档日期2018-06-22. Common Lisp Implementations: A Survey
  30. ^ [4] Comparison of actively developed Common Lisp implementations
  31. ^ An In-Depth Look at Clojure Collections, Retrieved 2012-06-24
  32. ^ Clojure rational. [27 August 2019]. Clojure is a Lisp not constrained by backwards compatibility 
  33. ^ Script-fu In GIMP 2.4, Retrieved 2009-10-29
  34. ^ librep at Sawfish Wikia, retrieved 2009-10-29
  35. ^ IEEE Scheme. IEEE 1178-1990 - IEEE Standard for the Scheme Programming Language. [27 August 2019]. 
  36. ^ The Jargon File - Lisp. [2006-10-13]. 
  37. ^ Sebesta, Robert W. "2.4 Functional Programming: LISP";"6.9 List Types";"15.4 The First Functional Programming Language: LISP". Concepts of Programming Languages (print) 10th. Boston, MA, USA: Addison-Wesley. 2012: 47–52;281–284;677–680. ISBN 978-0-13-139531-2 (英语). 
  38. ^ NB: a so-called "dotted list" is only one kind of "improper list". The other kind is the "circular list" where the cons cells form a loop. Typically this is represented using #n=(...) to represent the target cons cell that will have multiple references, and #n# is used to refer to this cons. For instance, (#1=(a b) . #1#) would normally be printed as ((a b) a b) (without circular structure printing enabled), but makes the reuse of the cons cell clear. #1=(a . #1#) cannot normally be printed as it is circular, although (a...) is sometimes displayed, the CDR of the cons cell defined by #1= is itself.
  39. ^ CSE 341: Scheme: Quote, Quasiquote, and Metaprogramming. Cs.washington.edu. 1999-02-22 [2013-11-15]. 
  40. ^ Quasiquotation in Lisp 互联网档案馆存檔,存档日期2013-06-03., Alan Bawden
  41. ^ Time of Evaluation - Common Lisp Extensions. Gnu.org. Retrieved on 2013-07-17.
  42. ^ 3.2.2.3 Semantic Constraints in Common Lisp HyperSpec
  43. ^ 4.3. Control Abstraction (Recursion vs. Iteration) in Tutorial on Good Lisp Programming Style by Kent Pitman and Peter Norvig, August, 1993.

延伸阅读

外部链接