Lisp 笔记
common lisp
断断续续更新中…
形式
统一用表这种形式来表示,不论是语句还是数据
对于语句,均以函数调用形式进行(少数除外)
形式为(函数名 参数 …)
求值
- 正确求值
返回求值结果
- 求值出错
报错(进入break loop)
防止求值
用 (quote 数据)来防止数据被求值可用‘数据的形式来简略表示
数据类型
- 整型
123
- 字符串
“abc” - 符号
abc
符号通常不返回自身,故调用时需 引用
- 列表
(a b c)
由于列表和函数调用形式一样,所以使用列表必须引用 ’(a b c)
**空表**和**假**一样 即 () 和 nil 是完全一样的列表操作
- cons
用以将一个元素加入一列表中
> (cons 'a '(b c))
(A B C)
需要注意的是,列表也可以作为元素。故这不是个连接两个列表的操作。
> (cons '(a) '(b c))
((A) B C)
- car
用以返回列表的第一个元素
> (car '(a b c))
A
- cdr
用以返回除第一个元素的子表
> (cdr '(a b c))
(B C)
- first second 和 third
分别用以返回列表中的第一、二、三个元素
> (fisrt '(a b c))
A
- list
用以创建一个列表
> (list 'a 'b 'c)
(A B C)
谓词
即返回真假的函数
- listp
检测参数是否为列表
> (listp '(a b))
T
> (listp ())
T
> (listp nil)
T
> (listp 'a)
NIL
可见,()和nil是一模一样的,都表示假,也都是列表
- null
空表测试
- not
逻辑取反
> (null ())
T
> (not nil)
T
> (not ())
T
> (null nil)
T
所以这两个其实是一样的
- numberp
检测参数是否为数字
> (numberp 'a)
NIL
> (numberp 1)
T
条件与逻辑
- if
由三个参数组成,参数缺省值为nil。
它与函数不同的地方在于:它并不会对每个参数都求值。
它会根据第一个参数(test表达式)的返回值来选择对第二个(then表达式)还是第三个参数(else表达式)求值。
> (if t
t
nil)
T
> (if nil
t
nil)
NIL
> (if (listp 'a)
"是列表"
"不是列表")
"不是列表"
> (if (listp '(a))
"是列表"
"不是列表")
"是列表"
> (if t
(+ 2 3))
5
> (if nil
(+ 2 3))
NIL
- and
求值规则与c语言类似,第一个参数为真才对第二个求值
> (and t (+ 2 3))
5
> (and nil (+ 2 3))
NIL
- or
也与c语言类似,第一个参数为假才对第二个参数求值
> (or t (+ 2 3))
T
> (or nil (+ 2 3))
5
函数
形式如下
(defun 函数名 (参数表)
操作)
> (defun my-third (x)
(car (cdr (cdr x)))
MY-THIRD
> (my-third '(a b c))
C
输入输出
- format
用于格式化输出
形式如下:
(format 输出位置 "格式字符串和输出内容" 变量)
;t表示输出到缺省位置,通常为顶层
> (format t "hello")
hello
NIL
;~A是变量占位符,~%是换行符
> (format t "~A + ~A = ~A ~%" 2 3 (+ 2 3))
2 + 3 = 5
NIL
- read
用于读取,缺省状态通常读取顶层
> (read)
a
A
变量
- let
用以定义局部变量
形式如下:
(let ((变量名 变量值) (变量名 变量值))
操作)
> (let ((val "php"))
(format t "~A是世界上最好的语言" val))
php是世界上最好的语言
NIL
- defparameter
用以定义全局变量,一般全局变量用*
包裹,如*glob*
> (defparameter *name* "Nahm")
*NAME*
> *name*
"Nahm"
- defconstant
用以定义全局常量
> (defconstant age 19)
AGE
> age
19
- boundp
返回参数是否为全局常量或者变量
注意参数须为符号故需引用
> (boundp '*name*)
T
赋值
- setf
给全局或局部变量赋值
> (setf *name* "Joe")
"Joe"
> *name*
> "Joe"
如果变量名不存在,则视为隐式定义全局变量。
setf参数为偶数个,故可写成:
> (setf x (list 'a 'b)
b 2
c 3)
第奇数个参数还可以为引用,如:
> (setf (car x) 'c)
> C
> x
> (C B)
函数式编程
- remove
接受一个对象和一个列表,返回一个不含这个值的列表
> (remove 'a '(a b c) )
> (B C)
如果真的想删除,则用返回值重新给原变量赋值即可。
函数式编程是基于返回值而不是函数副作用的编程的方式。
迭代
- do
第一个参数(变量列表):((变量 初始值 变化操作))
其中变量是符号,初始值和变化操作是表达式。初始状态,变量被赋予初始值,每次迭代被赋予变化操作的值。
第二个参数(列表)会被当作结束条件,即如果这个列表的第一个元素为真则结束。之后的参数会被依序求值。
整个do返回最后一个求值的部分。
例如,一个累加函数:
(defun acc (n)
(let ((val 0))
(do ((i 1 (+ i 1)))
((> i n) val)
(setf val (+ val i))
)
)
)
故此函数的递归形式为:
(defun acc (n)
(if (= n 1)
1
(+ n (acc (- n 1)))
)
)
- progn(工具函数)
接收任意数量表达式,依序求值,返回最后一个的值。
- dolist
遍历
(defun show-elem (lst)
(dolist (elem lst)
(format t "元素:~a~%" elem)
)
)
函数作为对象
- function
如果把函数名传给它,它会返回与函数名相关联的函数本身。就像 quote
一样。
如:
> (function +)
#<Compiled-Function + 17BA4E>
#'
是它的缩写,被成为升引号(sharp-quote)
- apply
我们可以把函数当参数传入函数中,apply就接收函数和实参列表作为参数,并返回执行结果。
如:
> (apply #'+ 1 2 '(3 4 5))
15
注意,apply要求最后一个参数必须为一个列表,但是其之前非函数的实参会与列表有同样的地位
- funcall
与apply做的事一样,只不过它不需要接收列表参数,直接可对函数后的实参传入所给函数。
如:
> (funcall #'+ 1 2 3)
6
lambda表达式
形如:(lambda (x) (+ x 100))
或 ((x) (+ x 100))
lambda字符的作用:
区分函数和列表(common lisp已经不需要显示区分)
由于函数是用列表来表示的,所以为了与普通列表做区分,列表第一个元素是lambda的是函数。
defun宏来创建一个函数并命名,但是函数不需要有名字,并且也不需要用defun来定义,我们可以直接引用函数。
所以我们可以直接引用lambda表达式来调用函数
如:
> ((lambda (x) (+ 1 x)) 1)
2
对lambda表达式做 function
操作获得函数本身
除以上操作外,lambda表达式还能用来写匿名函数
类型
在lisp中变量没有类型,故可以存储各种值。而值是有类型的,有点类似面向对象思想的继承。
比如数字 27 的类型依次是 fixnum、integer、rational、real、number、atom、t。t是所有类型的“基类”。
- typep
接受一个“对象”和一个类型,判断是否为该类型
如:
> (typep 27 'integer)
T