Blog Home
Updated: 2023 Oct 09

ANSI Common Lisp

第一章 基础知识

  • quote 作为一种引用,也就是什么也不做,它的另一种表达形式是’
  • list 函数用来创建列表
  • 空列表的两种表达形式,() nil
  • cons 用来构造列表,可以将多个列表合并为一个列表
  • car 和 cdr 是一对亲兄弟,car 用来取列表第一个元素,cdr 用来取除第一个的剩余元素
  • third 可以取第三个元素
  • t 代表逻辑真,与 nil 一样,都是对自身求值
  • listp 用来判断实参是否为列表,真则为 T ,假则为 NIL

函数的返回值将会被解释成逻辑 真 或逻辑 假 时,则称此函数为谓词(predicate) 在 Common Lisp 里,谓词的名字通常以 p 结尾。

  • null 和 not 用来判断实参是否为空
  • if 和 qunto 都是特殊操作符,不能用函数实现,因为实参被调用时永远会被求值,

    而 if 只有最后两个实参其中一个会被求值。

  • if 的最后一个实参是选择性的。如果忽略它的话,缺省值是 nil
  • 逻辑操作符 and 和 or ,两者都接受任意数量实参。如果所有实参同为真,and 则对能求值部分求值,否则为 nil, or 只要碰到一个为 真 的实参,则停止之后实参求值。
  • and 和 or 操作符其实是宏,宏和特殊操作符一样,可以绕过一般求值规则。
  • 用 defun 来定义函数
  • 符号是变量的名字,符号本身就是以对象的方式存在。这也是为什么符号,必须像列表一样被引用。 列表必须被引用,不然会被视为代码。符号必须要被引用,不然会被当作变量。
  • member 测试某个东西是否为列表成员
  • 谓词 eql 用来判断它的两个实参是否相等
  • 输出函数 format 接受两个或两个以上的实参,第一个决定打印到哪儿,第二个是字符串模版,剩余是要插入到字符串模板的参数。
  • format 第一个实参 t 表示输出到缺省位置,通常是顶层。字符串模板里 ~A 表示被填入的位置,~% 表示一个换行。
  • 输入函数 read 当没有实参时,会读取缺省位置,通常是顶层。read 是一个完整的 Lisp 解析器。
  • let 用来定义局部变量,由两部分组成,第一部分是一组创建新变量的指令,第二部分是一个表达式。
  • numberp 用来检测是否为数字
  • defparameter 用来创建全局变量,由符号和值两部分组成。为了避免与局部变量发生冲突,在命名时以星号作开始结束。
  • defconstant 用来定义一个全局常量。
  • boundp 函数用来判断某些符号是否是一个全局变量或常量。
  • setf 操作符用来给全局或局部变量赋值。
  • 如果 setf 的第一个实参是符号(symbol),且符号不是某个局部变量的名字,则 setf 把这个符号设为全局变量
  • 你不仅可以给变量赋值。传入 setf 的第一个实参,还可以是表达式或变量名。
  • 你可以给 setf 传入(偶数)个实参,等同于分别对其传参。
  • 函数 remove 接受一个对象和一个列表,返回不含这个对象的新列表。这里注意:remove 不是从列表内移除对象,因为原来的列表并没有改变。
  • 如果要真正移除列表内元素,可以和 setf 搭配,remove 后将数据重写会原列表。例如:(setf lst (remove 'a lst))
  • do 宏是 Common Lisp 里最基本的迭代操作符。和 let 类似, do 可以创建变量,而第一个实参是一组变量的规格说明列表。
  • progn 接受任意数量的表达式,依序求值,并返回最后一个表达式的值。
  • dolist 用来遍历列表元素
  • function 是一个特殊操作符,将函数名传给它可以返回相关联的对象,而且无需引用。
  • 如同 quote 可以用 ’ 代替,function 也可以用 #‘ 代替,#‘ 称为升引号。
  • apply 接受一个函数和实参列表,并返回把传入函数应用在实参列表的结果。可以接受任意多实参,只要左后一个实参时列表即可。
  • funcall 和 apply 类似,但无需将实参包装成列表。
  • lambda 不是操作符而是个符号
  • 要直接引用一个函数,我们使用所谓的lambda 表达式
  • lambda 表达式也可以是函数调用的第一个元素,例如: ((lambda (x) (+ x 100)) 1)
  • 通过在 lambda 表达式前面贴上 #' ,我们得到对应的函数。(funcall #'(lambda (x) (+ x 100)) 1)
  • lambda 还允许我们使用匿名函数
  • 函数 typep 接受一个对象和一个类型,然后判定对象是否为该类型
  • common lisp 的内置类型,对象总是不止属于某种类型。例如:数字21有 fixnum 、 integer 、 rational 、 real 、 number 、 atom 和 t 类型,t 为所有类型的基类。

第二章 列表

  • cons 把两个对象结合成一个有两部分的对象,称之为 Cons 对象。概念上来讲,一个 Cons 是一对指针;第一个是 car ,第二个是 cdr 。
  • 嵌套列表和平坦列表
  • consp 用来判断是否是 Cons 对象
  • 所有不是 Cons 对象的东西,就是一个原子(atom)
  • 注意:nil 既是一个原子,也是一个列表
  • eql 和 equal 注意区别,前者有点类似全等,有关指针或内存空间。
  • Lisp 没有指针,因为从概念上来讲每个值都是一个指针。
  • append 函数返回任何数目的列表串接。
  • (load "compress.lisp") 通过这种方式载入程序,注意,有些实现里的扩展名可能为.lsp
  • nth 函数可以找到列表特定位置的元素,它们是用 car 跟 cdr 定义的
  • nthcdr 函数可以找到第 n 个cdr
  • zerop 函数仅在参数为零时,才返回真
  • last 函数返回列表的最后一个 Cons 对象
  • caddr 函数,它是 cdr 的 cdr 的 car 的缩写(car of cdr of cdr)
  • cadr 函数可能会有异常产生
  • mapcar 接受一个函数以及一个或多个列表,并返回把函数应用至每个列表的元素的结果,直到有的列表没有元素为止
  • maplist 也是接受一个函数以及一个或多个列表,然后将列表渐进的下一个 cdr 传入函数
  • 其他还有 mapc 和 mapcan
  • Cons 对象可以想成是二叉树,car 代表左子树,而 cdr 代表右子树。
  • 操作数的函数通常有这种形式,car 和 cdr 同时做递归。这种函数被称为双重递归。
  • 列表是表示小集合的好方法,列表中的每个元素都代表了一个集合的成员
  • member 函数用来查找集合内的元素是否存在,存在则返回由寻找对象所开始的那部分
  • 一般情况,member 使用 eql 来比较对象。通过关键字作匹配,一个关键字是一个前面有冒号的符号
  • member 函数接受的关键字 :test 参数和 :key 参数
  • member-if 判断式可以通过类似 oddp 函数,来判断奇偶,奇数则返回真
  • adjoin 函数像是条件式的 cons。它接受一个对象及一个列表,如果对象还不是列表的成员,才构造对象至列表上
  • 集合论中的并集(union)、交集(intersection) 以及差集(complement)的实现,是由函数 union, intersection, set-difference
  • 因为集合中没有顺序的概念,所以返回的顺序可能不尽相同
  • 序列可以认为是一系列有特定顺序的对象,在 Common Lisp 里,序列包括列表与向量。
  • length 函数返回序列中元素的数目
  • subseq 函数用来复制序列的一部分,第二个(必须)参数是第一个开始引用进来的元素位置,第三个(可选)参数是第一个不引用进来的元素位置
  • reverse 函数返回顺序颠倒的序列
  • sort 排序函数接受一个序列及一个比较参数的函数,它是破坏性的,如果不想原序列被改动,传入一个副本。
  • 传入列表副本可以用 copy-list 函数
  • 函数 every 和 some 接受一个判断式及一个或多个序列。当仅输入一个序列时,他们测试序列元素是否满足判断式,当多于一个序列时,序列元素应该相同。
  • push 宏用于将元素放入列表前端
  • pop 宏用于将列表第一个元素移除,并返回这个元素
  • push 和 pop 都是由 setf 定义的,例如:(push obj lst) 等同于 (setf lst (cons obj lst))
  • pushnew 宏是 push 的变种,使用了 adjoin 而不是 cons
  • 点状列表,调用 list 所构造的列表,也可以称为正规列表,它可以是 NIL 或是 cdr 是正规列表的 Cons 对象
  • 一个由 Cons 对象组成的列表称为关联列表(assoclistor alist)
  • assoc 函数用来取出在关联列表中,与给定的键值有关联的 Cons 对。和 member 一样,接受关键字参数 :test 和 :key
  • assoc-if 类似 member-if

第三章 特殊数据结构

  • make-array 可以构造一个数组,第一个实参为一个指定数组维度的列表
  • Common Lisp 的数组至少可以达到七个维度,每个维度至少可以容纳 1023 个元素
  • 用 aref 取出数组内的元素,它是零索引的
  • 替换数组的某个元素,可以使用 setf 与 aref
  • 表示字面常量的数组,使用 #na 语法,其中 n 是数组的维度,例如:
#2a((b nil nil) (nil nil nil))
  • 一维数组又称向量(vector) 可以通过调用 vector 来构造及向量
  • 可以用 aref 来存取向量,但有一个更快的函数叫做 svref 专门用来存取向量。

Comments:

Email questions, comments, and corrections to hi@smartisan.dev.

Submissions may appear publicly on this website, unless requested otherwise in your email.