Common Lisp,缩写为CL(不要和缩写同为CL的组合逻辑混淆),作为Lisp的众多方言之一,由ANSI X3.226-1994(R1999)定义标准。它是为了标准化此前众多的Lisp分支而开发的,其本身并不是一个具体的实现,而是语言规范。市面上既有自由软件开源软件的实现,也有专有软件的实现。
相对于各种嵌入在特定产品中的语言Emacs Lisp和AutoLISP,Common Lisp是一个通用用途的编程语言。不像很多早期的Lisp,Common Lisp同Scheme一样,其中的变量是有作用域的。
Common Lisp是一种多重范式编程语言,这表现在:
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使用bignum来表示任意长度和精度的数值。分数类型精确地表示分数,这是很多语言都不具备的能力。Common Lisp自动将数值转换成适当的类型。
Common Lisp 字符类型并不仅仅是ASCII字符,这是因为Lisp在ASCII出现前就存在了。大多数现代的实现允许使用Unicode字符。
符号(Symbol)类型是Lisp语言共有的,而在其它语言中就较少见。一个符号是一个唯一的命名数据对象。与其它语言中的标识符(Identifier)类似,Lisp中的符号可以用作变量名,但它们也是可以独立使用的一种数据对象。一般来说,对一个符号求值时会得到以该符号为名的变量的值,但也有例外:形如:foo的关键词符号的值就是它本身,而符号T和NIL则一般被分别用于表示布尔真与布尔假。
Common Lisp中的序列类型包括列表、向量、位向量以及字符串。有很多可以对任意的序列类型起作用的操作符。
就像在几乎所有其它的Lisp方言中一样,Common Lisp中的列表由点对组成,有时候把点对叫做点对单元或序偶。一个点对是一种带有两个存储槽的数据结构,两个槽分别叫做它的car和cdr。一个列表就是一条点对的链表。每一个点对的car引用一个列表的成员(可能是另一个列表)。而除了最后一个的cdr引用到值nil之外,其余的每一个点对的cdr都引用下一个cons。点对也可以很容易地用于实现树或者其它复杂的数据结构;尽管一般建议使用结构体或者类的实例来代替它们。利用点对还可以创建环形数据结构。
Common Lisp支持多维数组,并且如果有必要可以动态地调整数组的大小。多维数组可以用于数学中的矩阵运算。向量是一维数组。数组可以携带任何类型的成员(甚至在同一个数组中混合不同类型)或指定为包含指定类型的成员,例如整数构成的向量。许多Lisp实现可以在使用指定类型的数组时对数组函数进行优化。有两种指定类型的数组是标准所包含的:字符串和位向量。字符串是由字符构成的向量,而位向量是由比特构成的向量。
哈希表存储数据对象之间的关联。任何值都可以用作哈希表的键或者值。哈希表和数组一样,可以根据需要自动调整大小。
包是符号的集合,主要用来将程序根据命名空间分成许多部分。一个包可能会导出一些符号,使这些符号成为公共接口的一部分。一个包可以使用其它的包。
Common Lisp中的结构体和C当中的结构体以及Pascal中的记录类似,表示带有任意数量和类型的字段(也叫做存储槽)的任何复杂的数据结构。Common Lisp中的结构体允许进行单一继承。
类和结构体类似,但是提供了更多的特性并且可以进行多重继承。类在后期才被整合进Common Lisp中并且和结构体存在一些概念上的重合。根据类所创建的对象叫做实例。一个特例是广义函数。广义函数既是函数又是实例。
在Common Lisp里,函数是一种数据类型。例如,可以写出以一个函数作为参数的函数,同时函数的返回值也可以是函数。这就让用函数描述非常通用的操作成为可能。
以至少一个函数作为输入、或者返回函数的函数称为高阶函数。Common Lisp的库很强的依赖这种高阶函数。例如,sort(排序)函数用一个比较操作符作为他的参数之一。这样一来,这个函数就不但可以用来对任何类型的数据排序,还可以根据一个关键码对数据结构排序。
(sort (list 5 2 6 3 1 4) #'>) ; Sorts the list using the > function as the comparison operator. ; Returns (6 5 4 3 2 1). (sort (list '(9 a) '(3 b) '(4 c)) #'< :key #'first) ; Sorts the list according to the first element of each sub-list. ; Returns ((3 b) (4 c) (9 a)).
这一求值模型对于函数很简单。当求值器遇到一式形如(F A1 A2...)
,那么名为F的符号被认为是如下之一:
lambda
开始的子式(sub-form)。如果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系统在函数输入时即被编译。)
函数的名字空间与数据变量的名字空间是分离的。这是Common Lisp和Scheme编程语言的一个重要不同之处。在函数名字空间定义名字的操作符包括defun
,flet
,和labels
。
要用函数名把函数作为参数传给另一个函数,必须使用function
特殊操作符,通常简略为#'。上文第一个sort
的例子中,为了引用在函数名字空间名为>
的函数,使用了代码#'>
。
Scheme编程语言的求值模型更简单些:因为只有一个名字空间,式(form)中所有位置都被求值(以任意顺序)-- 不仅是参数。所以以一种方言(dialect)写就的代码往往令熟悉其它方言程序员感到迷惑。例如,许多CL程序员喜欢使用描述性的变量名如"list"或"string",在Scheme中这将导致问题,因为它们可能局部覆盖了函数名字。
为函数提供分离的名字空间是否有益是Lisp社区不断争论的主题之一,常被称为“Lisp-1与Lisp-2辩论”。这些名称出现于Richard P. Gabriel和Kent Pitman 1998年的一篇论文,其中广泛的比较了这两种方法。[1]
哈希表是Common Lisp提供的用于存储“键值对”的数据类型。在哈希表中,任何的对象都可以作为键或者值。哈希表在必要的时候会自动调整大小。
Common Lisp中的宏是独一无二的,和C语言中的宏的机制相同,但是在宏扩展的过程中由于可以使用所有现有的Common Lisp功能,因此宏的功能就不再仅限于C语言中简单的文本替换,而是更高级的代码生成功能。宏的使用形式和函数一致,但是宏的参数在传递时不进行求值,而是以字面形式传递给宏的参数。宏的参数一旦传递完毕,就进行展开。展开宏的过程将一直进行到这段代码中的所有宏都展开完毕为止。宏完全展开完毕后,就和当初直接手写在此处的代码没有区别,也就是嵌入了这段代码上下文中,然后Lisp系统就对完整的代码上下文进行求值。
因为Common Lisp的宏在展开完毕后就完全嵌入了所处的代码上下文中,相当于以字面形式书写同样的代码,因此在宏展开代码中与上下文代码中相同的符号就会覆盖上面的引用,称为变量捕捉。
Common Lisp是由一份技术规范定义而不是被某一种具体实现定义(前者的例子有Ada语言和C语言,后者有Perl语言)。存在很多种实现,语言标准详细阐明了可能导致合理歧义的内容。
另外,各种实现试图引入库包来提供标准没有提及的功能。可移植的自由软件库提供了各种特性,Common-Lisp.net和Common Lisp Open Code Collection项目。
Common Lisp设计为由增量编译器实现。优化编译的标准声明(例如内联函数)已进入语言规范的计划。大多数Lisp实现将函数编译成原生的机器语言。其他的编译器编译为中间码,有损速度但是容易实现二进制代码的可移植。由于Lisp提供了交互式的提示符以及函数增量式的依次编译,很多人误会为Lisp是纯解释语言。
一些基于Unix的实现,例如CLISP,可以作为脚本解释器使用;因此,系统可以像调用Perl或者Unix shell解释器一样透明地调用它。
免费的可重发布实现包括:
商业实现在这里Franz, Inc.,Xanalys Corp.,Digitool, Inc.,Corman Technologies 和 Scieneer Pty Ltd.。
Common Lisp被用于很多成功的商业应用中,最著名的(毫无疑问要归功于Paul Graham的推广)要数Yahoo!商店的站点。其他值得一提的例子有:
也有很多成功的开源应用用Common Lisp写成,例如:
同样,Common Lisp也被许多政府和非盈利组织采用。NASA中的例子有:
|
SGML家族 |
|
||||||
---|---|---|---|---|---|---|---|
|