LISP:修订间差异
小 →top |
|||
第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的版本
编程范型 | 多范型:函数式,过程式,反射式,元编程 |
---|---|
設計者 | 约翰·麦卡锡 |
實作者 | 史帝芬·羅素, Timothy P. Hart和Mike Levin |
发行时间 | 1958年 |
型態系統 | 动态类型,强类型 |
衍生副語言 | |
Arc, AutoLISP, Clojure, Common Lisp, Emacs Lisp, ISLISP, Logo, newLISP, Racket, Scheme, SKILL | |
啟發語言 | |
IPL | |
影響語言 | |
CLU, Dylan, Falcon, Forth, Haskell, Io, Ioke, JavaScript, Julia[1], Lua, LPC, MDL,ML, Nu, OPS5, Perl,POP-2/11, Python, REBOL, Ruby, Smalltalk, Wolfram语言[2] |
Lisp(歷史上拼寫為LISP)是具有悠久歷史的計算機編程語言家族,有獨特和完全括號的前綴符號表示法[3]。起源於西元1958年,是現今第二悠久而仍廣泛使用的高階編程語言。只有FORTRAN編程語言比它更早一年[4][5]。Lisp編程語族已經演變出許多種方言。現代最著名的通用編程語種是Scheme、Common Lisp和Clojure。
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语言的主要现代版本包括Scheme、Common Lisp、Racket以及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,它被用在著名的AI系统SHRDLU之中。在1970年代,随着AI研究催生了商业分支,存有的Lisp系统的性能成为日益严重的问题。
谱系和方言
在它六十年的历史中,Lisp产生了在S-表达式语言的核心主旨上的很多变体。此外,每个给定方言有可能有多种实现,例如Common Lisp就有十余种以上的实现。
在方言间的差异可以是非常显眼的,例如Common Lisp使用关键字defun
来定义一个函数,而Scheme使用define
[14]。在一种标准化了的方言之内,符合标准的实现支持相同的核心语言,但是有着不同的扩展和函数库。
历史上的重要方言
- LISP 1[12],是第一个实现。
- LISP 1.5[15],是第一个广泛发行的版本,由McCarthy和其他人在MIT开发。如此命名是因为它包含了对最初的LISP 1解释器的改进,但不是LISP 2规划的那种重大重构。
- Stanford LISP 1.6[16],是斯坦福AI实验室开发的LISP 1.5后继者,广泛的发行于运行TOPS-10操作系统的PDP-10系统。它被Maclisp和InterLisp所淘汰。
- MACLISP[17],由MIT的MAC计划开发,MACLISP是LISP 1.5的直接后代。它运行在PDP-10和Multics系统上。MACLISP后来被叫做Maclisp,并通常被提及为MacLisp。在MACLISP中的“MAC”,既无关于Apple的Macintosh,又无关于McCarthy。
- Interlisp[18],由BBN科技开发,用于运行TENEX操作系统的PDP-10系统之上,后来InterLisp-D被Xerox Lisp机器接纳并昵称为西海岸Lisp。为基于6502的Atari 8位机家族计算机发行了叫做“InterLISP 65”的小型版本。在很长一段时间内,Maclisp和InterLisp相互之间是强有力的竞争者。
- Franz Lisp,起源于加利福尼亚大学伯克利分校的计划,后来由Franz Inc开发。这个名字是Franz Liszt的幽默变形,并且不涉及Allegro Common Lisp,它是Franz Inc近年来销售的Common Lisp。
- XLISP,AutoLISP基于了它。
- Standard Lisp和Portable Standard Lisp,被广泛使用和移植,特别是用它写成了计算机代数系统REDUCE。
- ZetaLisp,也叫做Lisp Machine Lisp,用在Lisp机器之上,是Maclisp的直接后代。ZetaLisp对Common Lisp有巨大影响。
- LeLisp,是一个法国Lisp方言。最早的界面建造器之一(叫做SOS界面[19])是用LeLisp写成的。
- Scheme(1975)[20]。
- Common Lisp(1984),是通过合并对Maclisp的不同尝试(ZetaLisp、Spice Lisp、NIL和S-1 Lisp)而创建的方言[21],也具有来自Scheme方言的实质影响。这个版本的Common Lisp在广泛的平台上都能获得到而被众人接受为业界标准[22],直至ANSI Common Lisp(ANSI X3.226-1994)出版。最广泛传播的Common Lisp子方言是Steel Bank Common Lisp(SBCL)、CMU Common Lisp(CMU-CL)、Clozure OpenMCL(不要混淆于Clojure)、GNU CLisp和后来版本的Franz Lisp;所有这些都坚持了后来的ANSI CL标准。
- Dylan,在它的第一个版本中是Scheme和Common Lisp对象系统的混合。
- EuLisp,尝试开发一个新的高效和整洁的Lisp。
- ISLISP,尝试开发一个新的高效和整洁的Lisp。标准化为ISO/IEC 13816:1997[23],后来修订为ISO/IEC 13816:2007[24]:《信息技术 – 编程语言,它们的环境和系统软件接口 – 编程语言 ISLISP》。
- IEEE Scheme,IEEE标准1178–1990(R1995)。
- ANSI Common Lisp,是美国国家标准协会(ANSI)的Common Lisp标准 ,由子委员会X3J13创建,其章程是[25]:以基础文档《Common Lisp: The Language》作为开始,致力于通过公开的达成共识过程,找到Common Lisp实现的程序可移植性和兼容性这个共通问题的解决方案。尽管形式上是ANSI标准,ANSI Common Lisp的实现、销售、使用和影响一直都是世界范围的。
- ACL2,是Common LISP的一个应用式(免于副作用)变体。ACL2既是可以建模计算机系统的编程语言,也是帮助证明这些模型性质的工具。
- Clojure,是一个Lisp的新近方言,它编译至Java虚拟机并特别关注并发性。
- Game Oriented Assembly Lisp(GOAL),是一个视频游戏编程语言,由Andy Gavin和Jak and Daxter团队在顽皮狗开发。它是使用Allegro Common Lisp写成并被用于整个Jak and Daxter系列游戏的开发。
- Chialisp,编译至CLVM的高级方言,是Chia区块链的链上编程环境。
2000年迄今
在20世紀90年代衰退之後,Lisp最近十年來因一些關注而逐漸復甦。大多數新活動都集中在Scheme、Common Lisp、Emacs Lisp、Racket和Clojure的實作上,包括開發新的跨平台函式庫和應用。當其他人認為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方言:Arc、Hy、Nu、Liskell和LFE。
Lisp編程語族時間軸
1955 | 1960 | 1965 | 1970 | 1975 | 1980 | 1985 | 1990 | 1995 | 2000 | 2005 | 2010 | 2015 | 2020 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
LISP I | LISP 1.5 | ||||||||||||
Maclisp | |||||||||||||
BBN Lisp | Interlisp | ||||||||||||
Scheme | IEEE 1178 | R5RS | R6RS | R7RS-small | |||||||||
Lisp Machine Lisp | |||||||||||||
Franz Lisp | |||||||||||||
Common Lisp | ANSI Common Lisp | ||||||||||||
Emacs Lisp | |||||||||||||
AutoLISP | Visual LISP | ||||||||||||
PicoLisp | |||||||||||||
newLISP | |||||||||||||
PLT Scheme | Racket | ||||||||||||
ISLISP | |||||||||||||
Clojure | |||||||||||||
Arc | |||||||||||||
LFE | |||||||||||||
Hy |
主要方言
Common Lisp和Scheme代表了Lisp发展的两个主流。这些语言体现了显著不同的设计选择。
Common Lisp是Maclisp的后继者。对它有重要影响的是Lisp Machine Lisp、Maclisp、NIL、S-1 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实现要求而持续的演化。
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解释器潜在的有更小大小,使得它特别流行于嵌入式脚本。例子包括SIOD和TinyScheme,二者都曾经以通用名字“Script-fu”成功的嵌入到了GIMP图像处理软件中[33]。LIBREP是John Harper最初基于Emacs Lisp语言开发的Lisp解释器,它已经嵌入到了Sawfish窗口管理器[34]。
标准化的方言
Lisp有官方标准化的方言:R6RS Scheme、R7RS Scheme、IEEE Scheme[35]、ANSI Common Lisp和ISO ISLISP。
语法和语义
- 注意:本文的例子是用Common Lisp书写(但是其多数在Scheme中也是有效的)。
符号表达式(S-表达式)
Lisp是一个面向表达式编程语言。不同于多数其他语言,在表达式和语句之间不做区分。所有代码和数据都写为表达式。当求值一个表达式的时候,它产生一个值(在Common Lisp中可能有多个值),它可以接着被嵌入到其他表达式中。每个值都可以是任何数据类型的。
McCarthy的1958年论文介入两种类型的语法:符号表达式(S-表达式或sexps),它镜像了代码和数据的内部表示;和元表达式(M-表达式),它表达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)
是其元素为三个原子1
、2
和foo
的一个列表。这些值是隐含的有类型的:它们分别是两个整数和叫做“符号”的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还提供逻辑算符and
、or
和not
。and
和or
算符进行短路求值,并分别返回它们的第一个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中,有两种基础数据类型:原子和列表。列表是元素的一个有限有序序列,这里的每个元素要么是一个原子要么是一个列表,而原子要么是一个数要么是一个符号。符号实质上是唯一性命名的项目,在源代码中写为字母数字串,并被要么用作一个变量名字要么符号处理中的一个数据项目。例如,列表(FOO (BAR 1) 2)
包含三个元素:符号FOO
、列表(BAR 1)
和数2
。
在原子和列表之间的本质区别是原子是不可变的和唯一性的。出现在源代码中不同位置的两个原子,如果以完全相同方式写成则表示相同的对象,而每个列表都是一个分立的对象,它们可以独立于其他列表而改变 ,并可以通过比较算符而区分于其他列表。
随着后来的Lisp介入了更多的数据类型,和编程风格的演化,原子的概念失去了重要性。很多方言出于遗产兼容性而保留了谓词atom
,定义它为对不是cons
的任何对象都为真。
cons和列表
Lisp列表被实现为单向链表[37]。这个链表的每个单元都叫做cons
(在Scheme中叫做pair
),它构成自两个指针,分别叫做car
和cdr
。
在众多可以用cons
单元构建的数据结构中,最基本一个叫做“真正列表”(proper list)。真正列表要么是特殊的nil
(空列表)符号,要么是一个cons
,它的car
指向一个数据项(它可以是另一个cons
结构比如一个列表),而cdr
指向另一个真正列表。
如果一个给定cons
被接受为一个链表的头部,那么它的car
指向这个列表的第一个元素,而它的cdr
指向这个列表的余下部份。为此,在提及作为链表(而非树等)一部份的cons
的时候,car
和cdr
函数也分别叫做first
和rest
。
因此,Lisp列表不是原子对象,它们类似C++或Java中的容器类的实例。一个列表就是链接的cons
的一个聚集。引用一个给定列表的变量,简单的就是到列表中第一个cons
的指针。遍历一个列表可以通过逐一cdr
这个列表来进行,就是说连续的选取cdr
来访问这个列表的每个cons
;或者通过使用任何一个将一个函数映射在一个列表之上的高阶函数。
由于cons
和列表在Lisp系统中是普遍性的,经常有人误解它们是Lisp的唯一数据结构。事实上,除了最简单者之外的所有Lisp都有其他数据结构,比如向量(数组)、散列表、结构等等。
S-表达式表示列表
圆括号的S-表达式表示了链表结构。有多种方式将相同的列表表示为一个S-表达式。cons
可以用“点对表示法”写为(a . b)
,这里的a
是car
而b
是cdr
。更长的真正列表可以用点对表示法写为(a . (b . (c . (d . nil))))
。这通常简写为列表表示法的(a b c d)
。一个非真正列表[38],可以用二者的组合来书写,比如列表(a b c . d)
有三个cons
,最后的cdr
是d
(也就是完全特殊形式下的(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)))
列表foo
和bar
分别是(a b c)
和(x b c)
。然而,尾部(b c)
在两个列表中都是相同的结构。它不是复件;对于两个列表指向b
和c
的cons
单元都在相同的内存位置。
共享结构而非复制可以得到相当客观的性能提升。但是这种技术可能以未预期的方式,交互于改变作为实际参数传递给它的列表的那些函数。改变一个列表,比如替代c
为goose
,将会影响另一个列表:
(setf (third foo) 'goose)
这变更foo
为(a b goose)
,但是从而还变更了bar
为(x b goose)
,这是可能是未预期的结果。这是缺陷的来源,为此改变它们的实际参数的那些函数被文档标示为破坏性(destructive)的。
函数式编程爱好者避免破坏性函数。在青睐函数式风格的Scheme方言中,破坏性函数的名字都标记了警告性感叹号,或者叫做“bang”,比如set-car!
(读作set car bang),它替换一个cons
的car
。在Common Lisp方言中,破坏性函数是司空见惯的,与set-car!
等价的是rplaca
,它的名字表示“replace car”。这个函数是不常见的,因为Common Lisp包括了一个特殊设施setf
,用来轻易的定义和使用破坏性函数。在Common Lisp中常见的风格是在构建原型的时候写函数式代码(没有破坏性调用),接着将破坏性调用作为优化增加于可以安全的进行它们的地方。
自求值形式和引述
Lisp求值用户录入的表达式。符号和列表求值为某个其他(通常更简单的)表达式,例如:一个符号求值为它指名的变量的值;(+ 2 3)
求值为5
。但是,多数其他形式求值为其自身:如果录入5
到Lisp中,它返回5
。
任何表达式都可以标记上防止被求值(对于符号和列表是需要的)。这是特殊算符quote
的角色,它也简写为'
(一个单引号)。例如,通常如果录入了符号foo
,它返回对应变量的值(没有这个变量则为一个错误)。要引用这个文字符号,录入要么(quote foo)
,要么更常见的'foo
。
Common Lisp和Scheme二者还支持“反引述”(backquote)算符(在Scheme中叫做准引述),这时录入`
字符(重音符)。它几乎同于普通引述,除了它允许表达式被求值,并将它们的值插入到引述列表之中,这些表达式标记了逗号,
表示去引述,或逗号-at,@
表示拼接算符。如果变量snue
有值(bar baz)
,则`(foo ,snue)
求值为(foo (bar baz))
,而`(foo ,@snue)
求值为(foo bar baz)
。反引述经常用于定义宏展开[39][40]。
自求值形式和引述形式是Lisp中文字的等价者。在程序代码中可以修改(可变)文字的值。例如,如果一个函数返回一个引述形式,而调用这个函数的代码修改这个形式,这可以改变这个函数在后续调用时的行为:
(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家族分裂为使用动态或使用静态(也叫做词法)作用域。Clojure、Common Lisp和Scheme缺省使用静态作用域,而newLISP、Picolisp和嵌入Emacs和AutoCAD的语言使用动态作用域。自从版本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
函数会很复杂是自然的,因为它必须也要实现所有特殊算符比如if
或lambda
。它们完成后,一个基本的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]。因此,不鼓励将尾递归风格作为使用更传统的迭代构造(比如do
、dolist
或loop
)的替代品[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)
当x
和y
指向相同的对象的时候返回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"
参见
参考文献
- ^ Introduction. The Julia Manual. Read the Docs. [2016-12-10]. (原始内容存档于2016-04-08).
- ^ Wolfram Language Q&A. Wolfram Research. [2016-12-10].
- ^ Edwin D. Reilly. Milestones in computer science and information technology. Greenwood Publishing Group. 2003: 156–157. ISBN 978-1-57356-521-9.
- ^ 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.
- ^ Conclusions. [2014-06-04]. (原始内容存档于2014-04-03).
- ^ 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.
- ^ The Top Programming Languages in Artificial Intelligence. Artificial Intelligence. APRO. [2021-02-15]. (原始内容存档于2020-10-30).
- ^ Paul Graham. Revenge of the Nerds. [2013-03-14].
- ^ Chisnall, David. Influential Programming Languages, Part 4: Lisp. 2011-01-12.
- ^ Jones, Robin; Maynard, Clive; Stewart, Ian. The Art of Lisp Programming. Springer Science & Business Media. December 6, 2012: 2. ISBN 9781447117193.
- ^ 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.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.
- ^ 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. Newsgroup: alt.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.
- ^ Common Lisp:
(defun f (x) x)
Scheme:(define f (lambda (x) x))
or(define (f x) x)
- ^ 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.
- ^ Quam, Lynn H.; Diffle, Whitfield. Stanford LISP 1.6 Manual (PDF).
- ^ Maclisp Reference Manual. March 3, 1979. (原始内容存档于2007-12-14).
- ^ Teitelman, Warren. InterLisp Reference Manual (PDF). 1974 [2006-08-19]. (原始内容 (PDF)存档于2006-06-02).
- ^ Outils de generation d’interfaces : etat de l’art et classification by H. El Mrabet
- ^ ftp://publications.ai.mit.edu/ai-publications/pdf/AIM-349.pdf[永久失效連結]
- ^ Steele, Guy L., Jr. Purpose. Common Lisp the Language 2nd. 1990. ISBN 0-13-152414-3.
- ^ Kantrowitz, Mark; Margolin, Barry. History: Where did Lisp come from?. FAQ: Lisp Frequently Asked Questions 2/7. 20 February 1996.
- ^ ISO/IEC 13816:1997. Iso.org. 2007-10-01 [2013-11-15].
- ^ ISO/IEC 13816:2007. Iso.org. 2013-10-30 [2013-11-15].
- ^ X3J13 Charter.
- ^ Chapter 1.1.2, History, ANSI CL Standard
- ^ [1] Clasp is a Common Lisp implementation that interoperates with C++ and uses LLVM for just-in-time compilation (JIT) to native code.
- ^ [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"
- ^ [3] 互联网档案馆的存檔,存档日期2018-06-22. Common Lisp Implementations: A Survey
- ^ [4] Comparison of actively developed Common Lisp implementations
- ^ An In-Depth Look at Clojure Collections, Retrieved 2012-06-24
- ^ Clojure rational. [27 August 2019].
Clojure is a Lisp not constrained by backwards compatibility
- ^ Script-fu In GIMP 2.4, Retrieved 2009-10-29
- ^ librep at Sawfish Wikia, retrieved 2009-10-29
- ^ IEEE Scheme. IEEE 1178-1990 - IEEE Standard for the Scheme Programming Language. [27 August 2019].
- ^ The Jargon File - Lisp. [2006-10-13].
- ^ 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 (英语).
- ^ 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.
- ^ CSE 341: Scheme: Quote, Quasiquote, and Metaprogramming. Cs.washington.edu. 1999-02-22 [2013-11-15].
- ^ Quasiquotation in Lisp 互联网档案馆的存檔,存档日期2013-06-03., Alan Bawden
- ^ Time of Evaluation - Common Lisp Extensions. Gnu.org. Retrieved on 2013-07-17.
- ^ 3.2.2.3 Semantic Constraints in Common Lisp HyperSpec
- ^ 4.3. Control Abstraction (Recursion vs. Iteration) in Tutorial on Good Lisp Programming Style by Kent Pitman and Peter Norvig, August, 1993.
延伸阅读
- McCarthy, John. The implementation of Lisp. History of Lisp. Stanford University. 1979-02-12 [2008-10-17].
- Steele, Jr., Guy L.; Richard P. Gabriel. The evolution of Lisp (PDF). The second ACM SIGPLAN conference on History of programming languages. New York, NY: ACM: 231–270. 1993 [2008-10-17]. ISBN 0-89791-570-4.
- Veitch, Jim. A history and description of CLOS. Salus, Peter H (编). Handbook of programming languages. Volume IV, Functional and logic programming languages first. Indianapolis, IN: Macmillan Technical Publishing. 1998: 107–158. ISBN 1-57870-011-6.
- Abelson, Harold; Sussman, Gerald Jay; Sussman, Julie. Structure and Interpretation of Computer Programs 2nd. MIT Press. 1996. ISBN 0-262-01153-0.
- My Lisp Experiences and the Development of GNU Emacs, transcript of Richard Stallman's speech, 28 October 2002, at the International Lisp Conference
- Graham, Paul. Hackers & Painters. Big Ideas from the Computer Age. O'Reilly. 2004. ISBN 0-596-00662-4.
- Berkeley, Edmund C.; Bobrow, Daniel G. (编). The Programming Language LISP: Its Operation and Applications (PDF). Cambridge, Massachusetts: MIT Press. March 1964.
- Article largely based on the LISP - A Simple Introduction chapter: Berkeley, Edmund C. THE PROGRAMMING LANGUAGE LISP: AN INTRODUCTION AND APPRAISAL. Computers and Automation. Sep 1964: 16-23.
- Weissman, Clark. LISP 1.5 Primer (PDF). Belmont, California: Dickenson Publishing Company Inc. 1967.
外部链接
- John McCarthy的主页 (页面存档备份,存于互联网档案馆)
- Paul Graham's The Roots of Lisp (页面存档备份,存于互联网档案馆)
- Racket官網 (页面存档备份,存于互联网档案馆)
- Lisp教學 (页面存档备份,存于互联网档案馆)
|