SBCL (Steel Bank Common Lisp) 是一个高性能的Common Lisp实现,它源自于CMUCL项目,是一个开源、免费且功能强大的Common Lisp开发环境。SBCL以其优秀的编译器和运行时系统而闻名,提供了高效的代码执行和丰富的调试功能。
SBCL支持多种操作系统,包括Windows、Linux和macOS。以下是不同平台的安装方法:
在基于Debian/Ubuntu的系统上,可以使用以下命令安装:
sudo apt-get update
sudo apt-get install sbcl
在基于Red Hat/Fedora的系统上,可以使用以下命令安装:
sudo dnf install sbcl
使用Homebrew安装:
brew install sbcl
从SBCL官方网站(http://www.sbcl.org/)下载Windows安装程序,然后按照安装向导进行安装。
注意:安装完成后,可以在命令行中输入sbcl
来启动SBCL REPL(Read-Eval-Print Loop,交互式解释环境)。
虽然SBCL自带了REPL环境,但对于更复杂的开发工作,推荐使用Emacs + SLIME(Superior Lisp Interaction Mode for Emacs)的组合,这是Common Lisp开发中最流行的环境。
Emacs是一个强大的文本编辑器,对Lisp有着天然的支持。安装方法如下:
# Debian/Ubuntu
sudo apt-get install emacs
# Red Hat/Fedora
sudo dnf install emacs
brew install emacs
从Emacs官方网站(https://www.gnu.org/software/emacs/)下载Windows版本并安装。
SLIME(Superior Lisp Interaction Mode for Emacs)是Emacs的一个模式,提供了强大的Common Lisp开发环境。安装步骤如下:
curl -O https://beta.quicklisp.org/quicklisp.lisp
sbcl --load quicklisp.lisp --eval '(quicklisp-quickstart:install)' --eval '(ql:add-to-init-file)' --quit
M-x package-install RET slime RET
(setq inferior-lisp-program "sbcl")
(load (expand-file-name "~/quicklisp/slime-helper.el"))
(slime-setup '(slime-fancy slime-tramp slime-asdf))
提示:配置完成后,在Emacs中输入M-x slime
即可启动SLIME环境,与SBCL进行交互。
Common Lisp使用前缀表达式(Prefix Notation)来表示代码和数据,所有的表达式都是S-表达式(Symbolic Expression),即括号包围的列表。这种统一的语法结构是Lisp语言的特色之一。
在Common Lisp中,有多种方式输出"Hello, World!":
"Hello, World!"
这种方式只是简单地返回字符串,并不是真正的输出。
(format t "Hello, World!")
这是最常用的输出方式,format
函数的第一个参数t
表示输出到标准输出(终端)。
(defun hello-world ()
(format t "Hello, World!"))
(hello-world)
这种方式定义了一个名为hello-world
的函数,然后调用它。
Common Lisp中,所有的代码都是表达式,每个表达式都会被求值。表达式可以是原子(如数字、字符串、符号)或列表。
;; 数字自求值
10 ; 返回 10
;; 字符串自求值
"Hello, World!" ; 返回 "Hello, World!"
;; 函数调用:列表的第一个元素是函数名,其余是参数
(+ 2 3) ; 返回 5
;; 嵌套表达式
(* (+ 2 3) (- 5 1)) ; 返回 20
使用defparameter
或defvar
定义全局变量:
(defparameter *pi* 3.14159) ; 定义全局变量*pi*
(defvar *name* "Common Lisp") ; 定义全局变量*name*
注意:按照Common Lisp的约定,全局变量通常以星号(*)包围,称为"耳蜗变量"(earmuffs)。
使用let
定义局部变量:
(let ((x 10)
(y 20))
(+ x y)) ; 返回 30
使用if
进行条件判断:
(if (> x 0)
(format t "x是正数")
(format t "x不是正数"))
使用cond
进行多条件判断:
(cond ((> x 0) (format t "x是正数"))
((< x 0) (format t "x是负数"))
(t (format t "x是零")))
使用loop
进行循环:
(loop for i from 1 to 10
do (format t "~a " i)) ; 输出 1 2 3 4 5 6 7 8 9 10
使用dolist
遍历列表:
(dolist (item '(a b c d))
(format t "~a " item)) ; 输出 A B C D
在Common Lisp中,函数是一等公民,可以像其他数据类型一样被传递和操作。使用defun
宏来定义函数。
使用defun
定义函数的基本语法:
(defun 函数名 (参数*)
"可选的函数描述"
函数体*)
(defun area (r)
"计算圆的面积"
(* pi r r))
;; 调用函数
(area 5) ; 返回 78.539816
使用&optional
关键字定义可选参数:
(defun make-rectangle (width &optional (height 10))
"创建一个矩形,高度默认为10"
(list width height))
(make-rectangle 5) ; 返回 (5 10)
(make-rectangle 5 8) ; 返回 (5 8)
使用&key
关键字定义关键字参数:
(defun make-person (name &key age (gender "unknown"))
"创建一个人,年龄和性别是可选的"
(list name age gender))
(make-person "Alice") ; 返回 ("Alice" NIL "unknown")
(make-person "Bob" :age 25) ; 返回 ("Bob" 25 "unknown")
(make-person "Charlie" :age 30 :gender "male") ; 返回 ("Charlie" 30 "male")
Common Lisp支持函数返回多个值,使用values
函数:
(defun divide (a b)
"返回除法的商和余数"
(values (floor a b) (mod a b)))
;; 调用函数
(divide 10 3) ; 返回 3 和 1
;; 使用multiple-value-bind获取多个返回值
(multiple-value-bind (quotient remainder)
(divide 10 3)
(format t "商: ~a, 余数: ~a~%" quotient remainder))
; 输出: 商: 3, 余数: 1
在Common Lisp中,函数可以作为参数传递给其他函数,也可以作为返回值。使用function
或简写#'
获取函数对象:
(defun apply-twice (fn x)
"将函数fn应用到x两次"
(funcall fn (funcall fn x)))
;; 定义一个平方函数
(defun square (x) (* x x))
;; 将square函数作为参数传递
(apply-twice #'square 3) ; 返回 81 (3的平方的平方)
使用lambda
创建匿名函数:
(funcall (lambda (x) (* x x)) 5) ; 返回 25
;; 与高阶函数结合使用
(mapcar (lambda (x) (* x x)) '(1 2 3 4 5)) ; 返回 (1 4 9 16 25)
列表是Lisp中最基本的数据结构,Lisp这个名字本身就来源于"LISt Processing"(列表处理)。Common Lisp提供了丰富的列表操作函数。
cons
函数用于构造列表,它接受两个参数:一个元素和一个列表,返回一个新列表,其中第一个参数是列表的第一个元素,第二个参数是列表的剩余部分。
(cons 'a '(b c d)) ; 返回 (A B C D)
(cons 'a '()) ; 返回 (A)
(cons 'a nil) ; 返回 (A)
car
函数返回列表的第一个元素,cdr
函数返回除第一个元素外的剩余部分。
(car '(a b c d)) ; 返回 A
(cdr '(a b c d)) ; 返回 (B C D)
注意:car
和cdr
的名称来源于历史原因,分别是"Contents of the Address part of the Register"和"Contents of the Decrement part of the Register"的缩写。现代Lisp实现中也提供了更直观的别名:first
(对应car
)和rest
(对应cdr
)。
可以组合使用car
和cdr
,形成如cadr
、cddr
等函数:
(cadr '(a b c d)) ; 等同于 (car (cdr '(a b c d))),返回 B
(cddr '(a b c d)) ; 等同于 (cdr (cdr '(a b c d))),返回 (C D)
(caddr '(a b c d)) ; 等同于 (car (cdr (cdr '(a b c d)))),返回 C
list
函数接受任意数量的参数,返回一个包含这些参数的列表:
(list 'a 'b 'c 'd) ; 返回 (A B C D)
(list 1 2 3 4) ; 返回 (1 2 3 4)
append
函数将多个列表连接成一个列表:
(append '(a b) '(c d)) ; 返回 (A B C D)
(append '(a b) '(c d) '(e)) ; 返回 (A B C D E)
nth
函数访问列表中的第n个元素(从0开始计数):
(nth 0 '(a b c d)) ; 返回 A
(nth 2 '(a b c d)) ; 返回 C
Common Lisp提供了一系列函数来访问列表的前几个元素:
(first '(a b c d)) ; 返回 A
(second '(a b c d)) ; 返回 B
(third '(a b c d)) ; 返回 C
(fourth '(a b c d)) ; 返回 D
mapcar
函数将一个函数应用到列表的每个元素,并返回结果列表:
(mapcar #'(lambda (x) (* x x)) '(1 2 3 4 5)) ; 返回 (1 4 9 16 25)
(mapcar #'list '(a b c) '(1 2 3)) ; 返回 ((A 1) (B 2) (C 3))
maplist
函数类似于mapcar
,但它将函数应用到列表的连续cdr上:
(maplist #'(lambda (x) x) '(a b c)) ; 返回 ((A B C) (B C) (C))
member
函数在列表中搜索一个元素,如果找到,返回从该元素开始的子列表;否则返回nil
:
(member 'b '(a b c d)) ; 返回 (B C D)
(member 'e '(a b c d)) ; 返回 NIL
find
函数在列表中搜索满足条件的元素,如果找到,返回该元素;否则返回nil
:
(find 'b '(a b c d)) ; 返回 B
(find-if #'evenp '(1 2 3 4 5)) ; 返回 2
remove-if
函数移除列表中满足条件的元素:
(remove-if #'evenp '(1 2 3 4 5)) ; 返回 (1 3 5)
remove-if-not
函数保留列表中满足条件的元素:
(remove-if-not #'evenp '(1 2 3 4 5)) ; 返回 (2 4)
reduce
函数将一个二元函数应用到列表的元素上,从左到右归约列表:
(reduce #'+ '(1 2 3 4 5)) ; 返回 15 (1+2+3+4+5)
(reduce #'* '(1 2 3 4 5)) ; 返回 120 (1*2*3*4*5)
本节提供一些简单的Common Lisp代码示例,帮助理解前面介绍的概念。
;; 递归实现阶乘
(defun factorial (n)
"计算n的阶乘"
(if (<= n 1)
1
(* n (factorial (- n 1)))))
;; 调用函数
(factorial 5) ; 返回 120
;; 递归实现斐波那契数列
(defun fibonacci (n)
"计算第n个斐波那契数"
(cond ((= n 0) 0)
((= n 1) 1)
(t (+ (fibonacci (- n 1))
(fibonacci (- n 2))))))
;; 调用函数
(fibonacci 10) ; 返回 55
;; 计算列表中所有偶数的和
(defun sum-even (lst)
"计算列表中所有偶数的和"
(reduce #'+ (remove-if-not #'evenp lst)))
;; 调用函数
(sum-even '(1 2 3 4 5 6 7 8 9 10)) ; 返回 30
;; 获取列表中的最大值
(defun my-max (lst)
"获取列表中的最大值"
(reduce #'(lambda (a b) (if (> a b) a b)) lst))
;; 调用函数
(my-max '(3 7 2 9 5)) ; 返回 9
;; 实现map函数
(defun my-map (fn lst)
"自定义map函数"
(if (null lst)
'()
(cons (funcall fn (first lst))
(my-map fn (rest lst)))))
;; 调用函数
(my-map #'(lambda (x) (* x x)) '(1 2 3 4 5)) ; 返回 (1 4 9 16 25)
;; 实现filter函数
(defun my-filter (fn lst)
"自定义filter函数"
(cond ((null lst) '())
((funcall fn (first lst))
(cons (first lst) (my-filter fn (rest lst))))
(t (my-filter fn (rest lst)))))
;; 调用函数
(my-filter #'evenp '(1 2 3 4 5 6 7 8 9 10)) ; 返回 (2 4 6 8 10)
;; 定义学生结构
(defstruct student
name
age
grade)
;; 创建学生列表
(defparameter *students*
(list (make-student :name "Alice" :age 20 :grade 90)
(make-student :name "Bob" :age 21 :grade 85)
(make-student :name "Charlie" :age 22 :grade 95)))
;; 按成绩筛选学生
(defun filter-by-grade (min-grade students)
"筛选成绩大于等于min-grade的学生"
(remove-if-not #'(lambda (s) (>= (student-grade s) min-grade))
students))
;; 调用函数
(filter-by-grade 90 *students*)
; 返回 (#S(STUDENT :NAME "Alice" :AGE 20 :GRADE 90)
; #S(STUDENT :NAME "Charlie" :AGE 22 :GRADE 95))
;; 计算平均成绩
(defun average-grade (students)
"计算学生的平均成绩"
(/ (reduce #'+ (mapcar #'student-grade students))
(length students)))
;; 调用函数
(average-grade *students*) ; 返回 90
提示:这些示例只是Common Lisp功能的冰山一角。Common Lisp是一门功能强大的编程语言,支持多种编程范式,包括函数式编程、面向对象编程等。继续探索和学习,你会发现更多有趣和强大的特性。