【发布时间】:2017-06-17 02:26:05
【问题描述】:
考虑以下代码:
CL-USER> (defmacro sum (a b)
(+ a b))
SUM
CL-USER> (let ((alpha 3) (beta -1))
(sum alpha beta))
; in: LET ((ALPHA 3) (BETA -1))
; (SUM ALPHA BETA)
;
; caught ERROR:
; during macroexpansion of (SUM ALPHA BETA). Use *BREAK-ON-SIGNALS* to intercept.
;
; Argument X is not a NUMBER: ALPHA
; (LET ((ALPHA 3) (BETA -1))
; (SUM ALPHA BETA))
;
; caught STYLE-WARNING:
; The variable ALPHA is defined but never used.
;
; caught STYLE-WARNING:
; The variable BETA is defined but never used.
;
; compilation unit finished
; caught 1 ERROR condition
; caught 2 STYLE-WARNING conditions
; Evaluation aborted on #<SB-INT:COMPILED-PROGRAM-ERROR {10030E4223}>.
基本上有两个原因(我能想到),导致此代码失败:
1. 宏sum 首先根据两个变量alpha 和beta 求值,它们作为符号发送到宏中。因此,宏内部要评估的代码是:
(+ 'alpha 'beta)
这行不通,因为我们不能添加两个符号。
2.变量alpha和beta是词法绑定的,因此宏的代码不能访问alpha和beta的符号值。
因此,重新定义sum:
CL-USER> (defmacro sum (a b)
(+ (symbol-value a) (symbol-value b)))
WARNING: redefining COMMON-LISP-USER::SUM in DEFMACRO
SUM
在这里,宏 sum 将评估提供给它的符号的值。只有在符号范围内,它才能做到这一点。因此,为了做到这一点,我们可以动态绑定alpha 和beta。
此外,为了检查动态绑定是否有效,我们可以创建一个函数dynamic-checker,定义如下:
CL-USER> (defun dynamic-checker ()
(+ alpha beta))
; in: DEFUN DYNAMIC-CHECKER
; (+ ALPHA BETA)
;
; caught WARNING:
; undefined variable: ALPHA
;
; caught WARNING:
; undefined variable: BETA
;
; compilation unit finished
; Undefined variables:
; ALPHA BETA
; caught 2 WARNING conditions
DYNAMIC-CHECKER
最后,我们可以在 REPL 中评估这段代码:
CL-USER> (let ((alpha 3) (beta -1))
(declare (special alpha))
(declare (special beta))
(print (dynamic-checker))
(sum alpha beta))
这给了我们错误:
; in: LET ((ALPHA 3) (BETA -1))
; (SUM ALPHA BETA)
;
; caught ERROR:
; during macroexpansion of (SUM ALPHA BETA). Use *BREAK-ON-SIGNALS* to intercept.
;
; The variable ALPHA is unbound.
;
; compilation unit finished
; caught 1 ERROR condition
2 ; Evaluation aborted on #<SB-INT:COMPILED-PROGRAM-ERROR {1003F23AD3}>.
CL-USER>
注意错误代码末尾的2。这是由函数dynamic-checker返回的,它添加了alpha和beta,尽管它们不是它的参数,这证明变量alpha和beta可以被@的成员动态访问987654345@.
因此,宏 sum 现在应该可以工作了,因为之前出现的两个问题都已解决。
但显然情况并非如此,我遗漏了一些东西。
任何帮助表示赞赏。
【问题讨论】:
-
宏中参数
A和B的值是符号ALPHA和BETA。通常宏扩展将在编译时发生,早在评估LET并建立绑定之前,因此代码无法工作。如果您使用解释模式,您也许可以对SYMBOL-VALUEs 求和,但您不应该依赖它。
标签: macros common-lisp dynamic-scope