1 CL中常见的宏
1.1 条件宏
when (defmacro when (cond &rest) `(if ,cond (progn ,@rest)))
unless (defmacro unless (cond &rest) `(if (not ,cond) (progn ,@rest)))
cond
1.2 逻辑宏
and
or
not
1.3 循环宏
CL中循环宏均是基于tagbody和go来实现的。
dolist (dolist (var lst) (body))
dotimes (dotimes (var count) (body))
do (do (variable*) (end-form return-form) statement*)而变量部分(var init-form step-form)
loop 基本语法为:(loop body*)
扩展归语法支持across,and,below,collectiong,counting,finally,for,from,summing,then,to等循环关键字。
2 宏展开期与运行期
当编译lisp代码时,会先将所有的宏展现并生成代码,然后才进行编译。宏运行期称为宏展开期,和运行期是不同的,后者是生成后的代码实际运行的阶段。
这一点很重要,因为宏展现期的代码与运行期的代码相比,运行环境完全不同,其无法访问那些仅存在于运行期的数据。
因此,总是向宏传递那些代表宏中sub-form的未求值lisp对象,宏的作用是生成代码,而非直接做任何事情。
3 defmacro
宏的定义和函数类似,使用defmacro来定义。
我们使用宏是希望将某种形式转换成代码;因此我们先要定义清楚宏调用的形式,以及宏展开后的形式。清楚这两点后,我们来写代码在这两种形式间转换。
4 宏形参
传递给宏的实参是代表调用宏时的源代码LISP对象,因此宏的第一步是提取出这些对象中用于展开的部分。
5 生成展开式
使用`语法来生成代码,它比'更强大,支持,和,@的求值。当读取器讲到一个`表达式时,它将其翻译成生成适当列表结构的代码。如`(,a b)生成(list a 'b)
在slime中,可以调用C-c ret来显示其宏展开形式。我们自己也可以使用macroexpand-1来展开。
6 堵住漏洞
宏可能以三种形式泄露其内部细节。第一种是重复计算;第二种是由于表达式的副作用,造成因求值顺序不同而结果出错;第三种是内部变量名覆盖外部变量。
这三个问题都比较容易解决。第一个问题,注意对于表达式只初始化一次;第二个问题是保证初始你给的顺序。第三个问题要使用gensym来生成唯一变量名。