Common Lisp

维基百科,自由的百科全书
跳转至: 导航搜索
Common Lisp
编程范型 多重编程范式
发行时间 1984, 1994 for ANSI Common Lisp
型態系統 动态类型强类型
作業系統 跨平台
許可證 GNU通用公共许可证Artistic License
網站 http://common-lisp.net

Common Lisp,缩写为CL(不要和缩写同为CL的組合邏輯混淆),作为Lisp的众多方言之一,由ANSI X3.226-1994(R1999)定义标准。它是为了标准化此前众多的Lisp分支而开发的,其本身并不是一个具体的实现,而是语言规范。市面上既有自由软件开源软件的实现,也有专有软件的实现。

相对于各种嵌入在特定产品中的语言Emacs LispAutoLISP,Common Lisp是一个通用用途的编程语言。不像很多早期的Lisp,Common Lisp同Scheme一样,其中的变量是有作用域的。

Common Lisp是一种多重范式编程语言,这表现在:

  • 支持各种编程技术:过程编程函数编程面向对象[需要消歧义]的编程。
  • 动态数据类型,但是可以使用可选的类型声明来提高效率和增强安全性。
  • 可以通过一些标准特性来扩展,例如Lisp宏(程序自我进行的编译时代码重排(compile-time code rearrangement accomplished by the program itself))和阅读器宏(赋予用户自定义的保留字以特殊意义的符号扩展(extension of syntax to give special meaning to characters reserved for users for this purpose))。

语法[编辑]

Common Lisp是一种Lisp;它使用S-表达式来表示代码和数据结构。函数调用以列表的形式写出,列表的第一项是函数名,就像在这些例子中:

(+ 2 2)           ;  将2加上2,得4

(setf p 3.1415)  ;设定变量p等于3.1415,pi是一个内置变量,不能用setf设置它
(defun square (x) (* x x)) ;定义一个函数用来计算一个数的平方:

 ;执行这个函数:
 (square 3)        ;返回9

資料型別[编辑]

Common Lisp 擁有豐富的資料型別。

純量型別[编辑]

數值型別包括整數,分數,浮點數和複數。Common Lisp使用大數(bignums)來表示任意大小和精度的數值。 分數型別確切地代表分數,很多語言並不具備這種能力。Common Lisp會自動將數值轉換成適當的型別。有許多方式取捨數值,函數round將參數四捨六入為最接近的整數,逢五則取偶整數。truncatefloorceiling分別朝向零,向下或向上取整數。所有這些函數將捨去的小數當作次要值返回。

例如,(floor -2.5)產生 -3, 0.5;(ceiling -2.5)產生 -2,-0.5;(round 2.5)得到 2,0.5;和(round 3.5)得到 4,-0.5。

Common Lisp字元型別不限於ASCII字符,因為在ASCII出現前Lisp就已經存在了。大多數現代實現允許Unicode字元。

符號(Symbol)型別是Lisp語言共有的,而在其它語言中較少見。一個符號是個命名唯一的資料物件,它擁有幾個部份:名稱,值,函數,屬性列表(property list)和套件。其中,值單元和函數單元是最重要的。Lisp中的符號通常類似於其它語言中的標識符(identifier)用法:保存變量的值;然而還有很多種用途。一般來說,對一個符號求值時會得到以該符號為變量名稱的值,但也有例外:譬如在關鍵符套件中的符號,形如:foo的符號值就是它本身(自我評估的,self-evaluating),而符號TNIL則用於表示布爾邏輯值真與假。Common Lisp可設計容納符號的命名空間,稱為“套件”(package)。

資料結構[编辑]

Common Lisp中的序列型別包括列表、向量、位元向量和字串。有許多函式可對任何序列型別進行操作。

Common Lisp如同幾乎所有的Lisp方言中,列表由點對(conses)組成,有時稱其為cons單元、序偶或構對。一個點對是帶有兩個儲存槽的資料結構,分別稱為car和cdr。一個列表就是一條點對的串列,或只是個空列表。每一個點對的CAR會參照列表的成員(可能是另一個列表)。而除了最後一個的CDR參照到nil值之外,其餘的CDR都會參照下一個點對。Conses也能輕易地實現樹和其他複雜的資料結構;儘管一般建議以結構體或是類別的實例來代替。利用點對能夠創建循環形的資料結構。

Common Lisp支援多維陣列,且如需要可動態地調整陣列的大小。多維陣列常用於數學中的矩陣運算。向量就是一維陣列。陣列可以載入任何型別(甚至可以用混合的型別)的成員,或只專用於包含特定型別的成員,例如整數構成的位元向量。許多Lisp實作會根據特定型別,對陣列的操作函數進行優化。兩種特定型別的專用陣列是標準的:字串和位元向量。字串是由許多字元構成的向量,而位元向量是由許多位元構成的向量。

雜湊表存儲資料對象之間的關聯,任何物件都可以作為雜湊表的鍵或值。和數組一樣,雜湊表可依需求自動調整其大小。

套件是一組符號的集合,主要用於將程序的個別部份區分命名空間。套件可以匯出一些符號,將它們作為共用介面的某一部份。 套件可以引用並承括其它套件中的符號。

Common Lisp 中的結構體(Structures)類似於C語言的structs和Pascal的records,是一種任由使用者發揮的複雜資料結構定義, 表示具有任意數量和任何型別的欄位(也叫做槽)。結構允許單一繼承。

類別(Class)在後期被整合進Common Lisp中,有些概念與結構體重疊,但類別提供了更多的動態特性和多重繼承(見 CLOS)。 由類別創建的物件稱為實例。有個特殊情況是泛型(Generics)的雙重角色,泛型既是函數,也是類別的實例物件。

函数[编辑]

Common Lisp支援第一類函數(亦即函數可作為資料類型來處理)。例如編寫以其他函數
當作一個函數的參數,或函數的傳回值也是函數, 使得利用函數來描述常用的操作成為可能。
Common Lisp函式庫高度依賴於這樣的高階函數變換。舉例而言,sort函數將比較運算符
作為參數,而且可以選用如何取關鍵的函數作為參數。這樣一來不僅可對任何型別的資料排序
還能根據取用的關鍵碼對資料結構進行排序。

 ;; 使用大小於函數作為比較關係,對列表進行排序。
 (sort (list 5 2 6 3 1 4) #'>)   ; 大於比較排序結果 (6 5 4 3 2 1)
 (sort (list 5 2 6 3 1 4) #'<)   ; 小於比較排序結果 (1 2 3 4 5 6)
 ;; 對每個子列表中,根據其第一個元素作為鍵值,以小於比較關係來排序。
 (sort (list '(9 A) '(3 B) '(4 C)) #'< :key #'first)   ; 結果為 ((3 B) (4 C) (9 A))

對函數求值的模型非常簡單。當求值器遇到一個形式如(F a1 a2 ...)時,
那麼名稱為F的符號會被假定是以下三種狀況之一:

  • 是否為基本操作符?(在固定列表中檢查,ANSI LISP標準有25個特殊操作符號)
  • 是否為宏運算符?(必須先前已經存有定義)
  • 是否為函數名稱?(預設),可以是符號,也可能是以lambda符號開頭的子形式。

如果F符號是三者其中之一,則求值器判定它是個函數,找到此函數的定義內容,
然後以從左到右的次序來評估參數a1,a2,...,an的值,並且使用這些值進行運算,
以函數定義中最後一個評估的結果作為傳回值。


定义函数[编辑]

defun用来定义函数。 函数定义给出了函数名,参数名和函数体:

(defun square(x)
   (* x x))

函数定义中可以包括“声明”,它可以指示编译器优化设置或参数的数据类型等。还可以在函数定义中包括“文档字符串”(docstring),Lisp系统用它们形成交互式文档:

(defun square(x)
   (declare (number x) (optimize (speed 3) (debug 0) (safety 1)))
   "Calculates the square of the number x."
   (* x x))

匿名函数用lambda表达式定义。Lisp编程频繁使用高阶函数,以匿名函数作为其参数的作法十分有效。

还有一些有关于函数定义和函数操作的运算符。如,操作符compile可以用来重新编译函数。(一些Lisp系统默认下在解释器里运行函数,除非指示编译它;其他Lisp系统在函数输入时即被编译。)


定義泛型函數及方法[编辑]

defgeneric宏用來定義泛型函數,而defmethod宏則用來定義方法。泛型函數是一些方法的集合。 方法可依照CLOS標準類別、系統類別、結構類別或物件,以特定方式處理它們所使用的參數。許多型別都有相對應的系統類別。 當呼叫泛型函數時,多樣派發(multiple-dispatch)將會依型別確定要應用的有效方法。如下列範例展示了 對不同型別的參數如數值、向量或字串,設計對應的add方法將兩個物件相加的動作。

 (defgeneric add (a b))
 (defmethod add ((a number) (b number))
    (+ a b))
 (defmethod add ((a vector) (b number))
    (map 'vector (lambda (n) (+ n b)) a))
 (defmethod add ((a vector) (b vector))
    (map 'vector #'+ a b))
(defmethod add ((a string) (b string))
  (concatenate 'string a b) )
 (add 2 3)                   ; returns 5
 (add #(1 2 3 4) 7)          ; returns #(8 9 10 11)
 (add #(1 2 3 4) #(4 3 2 1)) ; returns #(5 5 5 5)
 (add "COMMON " "LISP")      ; returns "COMMON LISP"

泛型函數也是第一類資料型別。除了上面陳述之外,泛型函數和方法還有更多的特性。


多值[编辑]

Common Lisp支援多值的概念,任何表達式經過評估之後必定會有一個主要值,但它也可能擁有任何數量的次要值,讓感興趣的呼叫者接收和檢查。這個概念與回傳列表值不同,因為次要值是備選用的,並通過專用的側面通道來傳遞。也就是說如果不需要次要值,則呼叫者完全不需要知道它們的存在,這是偶爾需使用額外而非必要的資訊,一個方便的機制。

  • 例如TRUNCATE函數對給定數值取最接近的整數。然而,它也會返回一個餘數作為次要值,使呼叫者確定有多少數值被捨棄了。它還支援可選用的除數參數,可顯明地表達带余除法
(let ((x 1266778)
      (y 458))
  (multiple-value-bind (quotient remainder)
      (truncate x y)
    (format nil "~A divided by ~A is ~A remainder ~A" x y quotient remainder)))

;;;; => "1266778 divided by 458 is 2765 remainder 408"
  • GETHASH回傳雜湊表中依鍵作搜尋的值,否則返回預設值,還有一個指出是否找到該值的布爾輔助值。因此不論搜尋結果(找到鍵的對應值或預設值)是否成功,源碼都可以直接使用它,但如果要求能區別搜尋結果的情況時,它可以檢查輔助的布爾值並做出適當反應。相同的函數調用支援兩種使用情境,不會受到另一個的負擔或約束影響。
(defun get-answer (library)
  (gethash 'answer library 42))

(defun the-answer-1 (library)
  (format nil "The answer is ~A" (get-answer library)))
;;;; Returns "The answer is 42" if ANSWER not present in LIBRARY

(defun the-answer-2 (library)
  (multiple-value-bind (answer sure-p)
      (get-answer library)
    (if (not sure-p)
        "I don't know"
     (format nil "The answer is ~A" answer))))
;;;; Returns "I don't know" if ANSWER not present in LIBRARY

一些標準形式支援多值,最常見的是用來存取次要值的MULTIPLE-VALUE-BIND基本運算子和用於返回多值的VALUES

(defun magic-eight-ball ()
  "Return an outlook prediction, with the probability as a secondary value"
  (values "Outlook good" (random 1.0)))

;;;; => "Outlook good"
;;;; => 0.3187

函数名字空间[编辑]

函数的名字空间与数据变量的名字空间是分离的。这是Common Lisp和Scheme编程语言的一个重要不同之处。在函数名字空间定义名字的操作符包括defun,flet,和labels

要用函数名把函数作为参数传给另一个函数,必须使用function特殊操作符,通常简略为#'。上文第一个sort的例子中,为了引用在函数名字空间名为>的函数,使用了代码#'>

Scheme编程语言的求值模型更简单些:因为只有一个名字空间,式(form)中所有位置都被求值(以任意顺序)-- 不仅是参数。所以以一种方言(dialect)写就的代码往往令熟悉其它方言程序员感到迷惑。例如,许多CL程序员喜欢使用描述性的变量名如"list"或"string",在Scheme中这将导致问题,因为它们可能局部覆盖了函数名字。

为函数提供分离的名字空间是否有益是Lisp社区不断争论的主题之一,常被称为“Lisp-1与Lisp-2辩论”。这些名称出现于Richard P. GabrielKent Pitman 1998年的一篇论文,其中广泛的比较了这两种方法。[1]

其它型別[编辑]

Common Lisp中的其他資料型別包括:

  • 雜湊是Common Lisp提供的用于存储“键值对”的資料型別。在雜湊中任何物件都可以作为键或者值。雜湊在必要时候會自动调整大小。
  • 路徑名稱(Pathnames)表示檔案系統中的檔案和目錄。Common Lisp的路徑名稱型別比大多數作業系統的檔名慣例更為通用,提高Lisp編程在不同系統存取檔案能力的可攜性。
  • 輸入流和輸出流(Input/output streams)表示二進位或文本資料的源頭和出口,例如顯示終端或開啟的檔案內容。
  • Common Lisp有內建的偽亂數產生器(PRNG)。隨機狀態物件代表可重複使用的偽亂數起源,允許用戶設定PRNG種子或使其重置序列。
  • 條件(Conditions)是用於表示程式回應的錯誤、異常和其它“有趣”事件的型別。
  • 類別是第一類物件,它們自身是被稱為元類別(metaclass)的實例。
  • 讀取字表(Readtables)是控制Common Lisp直譯器(read函)如何解析源碼文本的物件型別。開發人員可操控Lisp編程在讀取源碼時要使用哪一個讀取字表,改變或擴展Lisp的語法。

[编辑]

Common Lisp中的宏是独一无二的,和C语言中的宏的机制相同,但是在宏扩展的过程中由于可以使用所有现有的Common Lisp功能,因此宏的功能就不再仅限于C语言中简单的文本替换,而是更高级的代码生成功能。宏的使用形式和函数一致,但是宏的参数在传递时不进行求值,而是以字面形式传递给宏的参数。宏的参数一旦传递完毕,就进行展开。展开宏的过程将一直进行到这段代码中的所有宏都展开完毕为止。宏完全展开完毕后,就和当初直接手写在此处的代码没有区别,也就是嵌入了这段代码上下文中,然后Lisp系统就对完整的代码上下文进行求值。

变量捕捉和覆盖[编辑]

因为Common Lisp的宏在展开完毕后就完全嵌入了所处的代码上下文中,相当于以字面形式书写同样的代码,因此在宏展开代码中与上下文代码中相同的符号就会覆盖上面的引用,称为变量捕捉

同其他Lisp的比较[编辑]

实现[编辑]

Common Lisp是由一份技术规范定义而不是被某一种具体实现定义(前者的例子有Ada语言C语言,后者有Perl语言)。存在很多种实现,语言标准详细阐明了可能导致合理歧义的内容。

另外,各种实现试图引入库包来提供标准没有提及的功能。可移植的自由软件库提供了各种特性,Common-Lisp.netCommon Lisp Open Code Collection项目。

Common Lisp设计为由增量编译器实现。优化编译的标准声明(例如内联函数)已进入语言规范的计划。大多数Lisp实现将函数编译成原生的机器语言。其他的编译器编译为中间码,有损速度但是容易实现二进制代码的可移植。由于Lisp提供了交互式的提示符以及函数增量式的依次编译,很多人误会为Lisp是纯解释语言。

一些基于Unix的实现,例如CLISP,可以作为脚本解释器使用;因此,系统可以像调用Perl或者Unix shell解释器一样透明地调用它。

实现的列表[编辑]

免费的可重发布实现包括:

商业实现在这里Franz, Inc.Xanalys Corp.Digitool, Inc.Corman TechnologiesScieneer Pty Ltd.

应用[编辑]

Common Lisp被用于很多成功的商业应用中,最著名的(毫无疑问要归功于Paul Graham的推广)要数Yahoo!商店的站点。其他值得一提的例子有:

  • Orbitz,以飞机票预订为主的站点
  • MiraiIzware LLC's fully integrated 2d/3d computer graphics content creation suite that features what is almost universally regarded as the best polygonal modeler in the industry, an advanced IK/FK and non-linear animation system (later popularized by such products as Sega's Animanium and Softimage XSI, respectively), and advanced 2d and 3d painting. It is used in major motion pictures(most famously in New Line Cinema's Lord of the Rings), video games and military simulations.
  • Piano,一个用Lisp写的商业的航空期前期设计包以及与它的竞争对手的比较
  • Xanalys Corp.的调查软件,被全球的警察,安全部门和防止诈骗服务部门采用
  • Genworks International的多用途说明语言(GDL),是一个基于CL的开发工具,用来创建基于web的工程,设计和商业应用

也有很多成功的开源应用用Common Lisp写成,例如:

同样,Common Lisp也被许多政府和非盈利组织采用。NASA中的例子有:

外部链接[编辑]