您可能知道,Lisp 复合形式通常是从外向内处理的。您必须查看最外层嵌套的第一个位置的符号才能理解形式。该符号完全决定了形式的含义。以下表达式均包含(b c)
具有完全不同的含义;因此,我们无法通过分析来理解它们(b c)
首先部分:
;; Common Lisp: define a class A derived from B and C
(defclass a (b c) ())
;; Common Lisp: define a function of two arguments
(defun a (b c) ())
;; add A to the result of calling function B on variable C:
(+ a (b c))
传统上,Lisp 方言将形式分为运算符形式和函数调用形式。运算符形式具有完全任意的含义,由编译或解释该函数的代码段决定(例如,计算简单地递归函数调用的所有参数形式,并将结果值传递给函数)。
从早期历史开始,Lisp 就允许用户编写自己的运算符。对此存在两种方法:解释运算符(历史上称为fexprs
) 和称为宏的编译运算符。两者都围绕一个函数的思想,该函数接收未评估的形式作为参数,以便它可以实现自定义策略,从而用新的行为扩展评估模型。
A fexpr
类型运算符只是在运行时传递表单以及一个环境对象,通过它可以查找变量的值等。然后该操作员遍历表单并实现行为。
宏运算符在宏扩展时收到表单(这通常发生在读取顶级表单时,就在评估或编译它们之前)。它的工作不是解释表单的行为,而是通过生成代码来翻译它。 IE。宏是一个迷你编译器。 (生成的代码可以包含更多宏调用;宏扩展器将处理这一点,确保所有宏调用都被减少。)
The fexpr
这种方法失宠了,很可能是因为它效率低下。它基本上使编译变得不可能,而 Lisp 黑客则重视编译。 (早在 1960 年左右,Lisp 就已经是一种编译语言了。)fexpr
该方法对词汇环境也充满敌意;它需要fexpr
,它是一个函数,能够查看调用它的形式的变量绑定环境,这是词法作用域不允许的一种封装违规。
宏编写稍微困难一些,并且在某些方面不如 fexprs 灵活,但是从 20 世纪 60 年代到 70 年代,Lisp 对宏编写的支持得到了改进,使其接近尽可能简单。宏最初接收了整个表单,然后必须自己解析它。宏定义系统发展成为一种为宏函数提供参数的系统,这些参数以易于理解的形式接收分解的语法,包括语法的一些嵌套方面。还开发了用于编写代码模板的反引号语法,使表达代码生成变得更加容易。
那么回答你的问题,我自己怎样才能编写这样的表格呢?例如如果:
;; Imitation of old-fashioned technique: receive the whole form,
;; extract parts from it and return the translation.
;; Common Lisp defmacro supports this via the &whole keyword
;; in macro lambda lists which lets us have access to the whole form.
;;
;; (Because we are using defmacro, we need to declare arguments "an co &optional al",
;; to make this a three argument macro with an optional third argument, but
;; we don't use those arguments. In ancient lisps, they would not appear:
;; a macro would be a one-argument function, and would have to check the number
;; of arguments itself, to flag bad syntax like (my-if 42) or (my-if).)
;;
(defmacro my-if (&whole if-form an co &optional al)
(let ((antecedent (second if-form)) ;; extract pieces ourselves
(consequent (third if-form)) ;; from whole (my-if ...) form
(alternative (fourth if-form)))
(list 'cond (list antecedent consequent) (list t alternative))))
;; "Modern" version. Use the parsed arguments, and also take advantage of
;; backquote syntax to write the COND with a syntax that looks like the code.
(defmacro my-if (antecedent consequent &optional alternative)
`(cond (,antecedent ,consequent) (t ,alternative))))
这是一个合适的例子,因为 Lisp 最初只有cond
。没有if
在麦卡锡的 Lisp 中。 “语法糖”是后来发明的,可能是作为一个宏扩展到cond
, 就像my-if
above.