common-lisp开始使用common-lisp


备注

这是Common Lisp中的一个简单的hello world函数。示例将打印文本Hello, World! (不带引号;后跟换行符)到标准输出。

Common Lisp是一种编程语言,主要使用称为REPL的接口进行交互式使用。 REPL(读取评估打印循环)允许用户输入代码,对其进行评估(运行)并立即查看结果。 CL-USER>指示REPL的提示(此时输入一个类型的代码)。有时除了CL-USER之外的其他内容将出现在>之前,但这仍然是一个REPL。

提示后出现一些代码,通常是单个单词(即变量名)或表单( ()之间包含的单词/表单列表)(即函数调用或声明等)。在下一行将是程序打印的任何输出(如果程序没有打印任何内容,则为任何内容),然后是通过计算表达式返回的值。通常,表达式返回一个值,但如果它返回多个值,则每行出现一次。

版本

发布日期
Common Lisp 1984-01-01
ANSI Common Lisp 1994-01-01

基本表达

让我们在REPL中尝试一些基本表达式:

CL-USER> (+ 1 2 3)
6
CL-USER> (- 3 1 1)
1
CL-USER> (- 3)
-3
CL-USER> (+ 5.3 (- 3 2) (* 2 2))
10.3
CL-USER> (concatenate 'string "Hello, " "World!")
"Hello, World!"
CL-USER> 
 

Common Lisp程序的基本构建块是表单 。在这些示例中,我们有函数形式 ,即表达式,写为列表,其中第一个元素是运算符(或函数),其余元素是操作数(这称为“前缀表示法”或“波兰表示法”) “)。在REPL中编写表单会导致他们的评估。在示例中,您可以看到简单的表达式,其参数是常量,字符串和符号(在'string ,它是类型的名称)的情况下。您还可以看到算术运算符可以使用任意数量的参数。

值得注意的是,括号是语法的组成部分,不能像其他编程语言一样自由使用。例如,以下是一个错误:

(+ 5 ((+ 2 4)))
> Error: Car of ((+ 2 4)) is not a function name or lambda-expression. ...
 

在Common Lisp中,形式也可以是数据,符号,宏形式,特殊形式和lambda形式。它们可以被编写为被评估,返回零,一个或多个值,或者可以在宏的输入中给出,以其他形式转换它们。

Common Lisp学习资源

在线书籍

这些是可以在线免费访问的书籍。

在线参考

离线书籍

这些是您可能必须购买或从图书馆借出的书籍。

在线社区

图书馆

预先打包的Lisp环境

这些是易于安装和使用的Lisp编辑环境,因为您需要的所有内容都是预打包和预配置的。

  • Portacle是一个可移植的多平台Common Lisp环境。它提供了一个稍微定制的Emacs与Slime,SBCL(一种流行的Common Lisp实现),Quicklisp和Git。无需安装,因此这是一种非常快速简便的方法。
  • Lispbox是一个IDE(Emacs + SLIME),Common Lisp环境(Clozure Common Lisp)和库管理器(Quicklisp),预先打包为Windows,Mac OSX和Linux的档案。 “Lisp in a Box”的后代在Practical Common Lisp书中推荐。
  • 未预先打包,但SLIME将Emacs转换为Common Lisp IDE,并提供用户手册以帮助您入门。需要单独的Common Lisp实现。

Common Lisp实现

本节列出了一些常见的CL实现及其手册。除非另有说明,否则这些是免费软件实现。另请参阅Cliki的免费软件Common Lisp Implementations 列表 ,以及Wikipedia的商业Common Lisp实现列表

你好,世界

以下是来自Common Lisp的REPL会话的摘录,其中包含“Hello,World!”定义并执行函数。有关REPL的更全面说明,请参阅本页底部的备注。

CL-USER> (defun hello ()
           (format t "Hello, World!~%"))
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> 
 

这定义了名为hello 的零参数的“函数”,它将写入字符串"Hello, World!" 然后是标准输出的换行符,并返回NIL

定义我们编写的函数

(defun name (parameters...)
  code...)
 

在这种情况下,该函数称为hello ,不带参数,它运行的代码是执行一个函数调用。来自lisp函数的返回值是要运行的函数中的最后一位代码,因此hello 返回任何内容(format t "Hello, World!~%") 返回。

在lisp中调用一个函数,一个写(function-name arguments...) ,其中function-namefunction-namearguments...arguments... 的(空格分隔的)列表。有一些特殊情况看起来像函数调用但不是,例如,在上面的代码中没有被调用的defun 函数,它被特殊处理并改为定义函数。

在REPL的第二个提示符下,在我们定义了hello 函数之后,我们通过编写(hello) 调用它而没有参数。这反过来将使用参数t"Hello, World!~%" 调用format 函数。 format 函数根据给出的参数生成格式化输出(有点像C中高级版本的printf )。第一个参数告诉它输出到哪里, t 表示标准输出。第二个参数告诉它要打印什么(以及如何解释任何额外的参数)。该指令(第二个参数中的特殊代码) ~% 告诉格式打印换行符(即在UNIX上它可能会写入\n 并在windows \r\n )。格式通常返回NIL (在其他语言中有点像NULL )。

在第二个提示之后,我们看到Hello, World 已经打印,并且在下一行显示返回值为NIL

你好,姓名

这是一个稍微更高级的示例,它显示了常见的lisp的一些更多功能。我们从一个简单的Hello, World! 功能并在REPL上展示一些交互式开发。请注意分号中的任何文本; ,对其余部分是评论。

CL-USER> (defun hello ()
       (format t "Hello, World!~%")) ;We start as before
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> (defun hello-name (name) ;A function to say hello to anyone
       (format t "Hello, ~a~%" name)) ;~a prints the next argument to format
HELLO-NAME
CL-USER> (hello-name "Jack")
Hello, Jack
NIL
CL-USER> (hello-name "jack") ;doesn't capitalise names
Hello, jack
NIL
CL-USER> (defun hello-name (name) ;format has a feature to convert to title case
       (format t "Hello, ~:(~a~)~%" name)) ;anything between ~:( and ~) gets it
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello-name "jack")
Hello, Jack
NIL
CL-USER> (defun hello-name (name)
       (format t "Hello, ~:(~a~)!~%" name))
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello-name "jack") ;now this works
Hello, Jack!
NIL
CL-USER> (defun hello (&optional (name "world")) ;we can take an optional argument
       (hello-name name)) ;name defaults to "world"
WARNING: redefining COMMON-LISP-USER::HELLO in DEFUN
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> (hello "jack")
Hello, Jack!
NIL
CL-USER> (hello "john doe") ;note that this capitalises both names
Hello, John Doe!
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~a ~r" name number)) ;~r prints a number in English
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16) ;this doesn't quite work
Hello, Louis sixteen
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~:(~a ~:r~)!" name number)) ;~:r prints an ordinal
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16)
Hello, Louis Sixteenth!
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~:(~a ~@r~)!" name number)) ;~@r prints Roman numerals
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16)
Hello, Louis Xvi!
NIL
CL-USER> (defun hello-person (name &key (number)) ;capitalisation was wrong
       (format t "Hello, ~:(~a~) ~:@r!" name number))
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16) ;thats better
Hello, Louis XVI!
NIL
CL-USER> (hello-person "Louis") ;we get an error because NIL is not a number
Hello, Louis ; Evaluation aborted on #<SB-FORMAT:FORMAT-ERROR {1006641AB3}>.
CL-USER> (defun say-person (name &key (number 1 number-p)
                                      (title nil) (roman-number t))
       (let ((number (if number-p
                 (typecase number
                   (integer
                (format nil (if roman-number " ~:@r" " ~:(~:r~)") number))
                   (otherwise
                (format nil " ~:(~a~)" number)))
                 "")) ; here we define a variable called number
         (title (if title 
                (format nil "~:(~a~) " title)
                ""))) ; and here one called title
         (format nil "~a~:(~a~)~a" title name number))) ;we use them here

SAY-PERSON
CL-USER> (say-person "John") ;some examples
"John"
CL-USER> (say-person "john doe")
"John Doe"
CL-USER> (say-person "john doe" :number "JR")
"John Doe Jr"
CL-USER> (say-person "john doe" :number "Junior")
"John Doe Junior"
CL-USER> (say-person "john doe" :number 1)
"John Doe I"
CL-USER> (say-person "john doe" :number 1 :roman-number nil) ;this is wrong
"John Doe First"
CL-USER> (defun say-person (name &key (number 1 number-p)
                                      (title nil) (roman-number t))
       (let ((number (if number-p
                 (typecase number
                   (integer
                (format nil (if roman-number " ~:@r" " the ~:(~:r~)") number))
                   (otherwise
                (format nil " ~:(~a~)" number)))
                 ""))
         (title (if title 
                (format nil "~:(~a~) " title)
                "")))
         (format nil "~a~:(~a~)~a" title name number)))
WARNING: redefining COMMON-LISP-USER::SAY-PERSON in DEFUN
SAY-PERSON
CL-USER> (say-person "john doe" :number 1 :roman-number nil) ;thats better
"John Doe the First"
CL-USER> (say-person "louis" :title "king" :number 16 :roman-number nil)
"King Louis the Sixteenth"
CL-USER> (say-person "louis" :title "king" :number 16 :roman-number t)
"King Louis XVI"
CL-USER> (defun hello (&optional (name "World") &rest arguments) ;now we will just
       (apply #'hello-name name arguments)) ;pass all arguments to hello-name
WARNING: redefining COMMON-LISP-USER::HELLO in DEFUN
HELLO
CL-USER> (defun hello-name (name &rest arguments) ;which will now just use
       (format t "Hello, ~a!" (apply #'say-person name arguments))) ;say-person
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello "louis" :title "king" :number 16) ;this works now
Hello, King Louis XVI!
NIL
CL-USER>
 

这突出了Common Lisp format 函数的一些高级功能,以及可选参数和关键字参数(例如:number )等一些功能。这也提供了在常见的lisp中REPL的交互式开发的示例。

Lambda表达式和匿名函数

可以通过Lambda表达式定义匿名函数而无需使用名称。为了定义这些类型的函数,使用关键字lambda 而不是关键字defun 。以下行都是等效的,并定义了输出两个数字之和的匿名函数:

(lambda (x y) (+ x y))
(function (lambda (x y) (+ x y)))
#'(lambda (x y) (+ x y))
 

在创建Lambda表单时 ,它们的用处是显而易见的,即一个表单,其中第一个元素是lambda表达式,其余元素是匿名函数的参数。示例( 在线执行 ):

(print ((lambda (x y) (+ x y)) 1 2)) ; >> 3

(print (mapcar (lambda (x y) (+ x y)) '(1 2 3) '(2 -5 0))) ; >> (3 -3 3)
 

整数列表的总和

(defun sum-list-integers (list)
    (reduce '+ list))

; 10
(sum-list-integers '(1 2 3 4))

; 55
(sum-list-integers '(1 2 3 4 5 6 7 8 9 10))
 

REPL中的简单Hello World程序

Common Lisp REPL是一个交互式环境。评估提示后编写的每个表单,然后将其值作为评估结果打印。所以Common Lisp中最简单的“Hello,World!”程序是:

CL-USER> "Hello, World!"
"Hello, World!"
CL-USER>
 

这里发生的是在REPL的输入中给出字符串costant,它被计算并打印结果。从这个例子可以看出,字符串,如数字,特殊符号,如NILT 以及一些其他文字,都是自我评估的形式:即他们自己评估。