当您在其上创建闭包时,将捕获非特殊变量的环境:
(let ((x 1))
(let ((f (lambda () x)))
(let ((x 2))
(eql 2 (funcall f)))))
;;=> NIL
特殊变量的词法环境不会:
(defvar *x*) ; *x* is special
(let ((*x* 1))
(let ((f (lambda () *x*)))
(let ((*x* 2))
(eql 2 (funcall f)))))
;;=> T
使用这种方法,您可以轻松定义一个宏,该宏将扩展为与之前类似的代码,让您确定符号是否全局声明为特殊:
(defmacro specialp (symbol)
(let ((f (gensym "FUNC-")))
`(let ((,symbol 1))
(let ((,f (lambda () ,symbol)))
(let ((,symbol 2))
(eql 2 (funcall ,f)))))))
(specialp x) ;=> NIL
(specialp *x*) ;=> T
请注意,这不是函数,而是宏。这意味着使用 symbols X 和 *X* 调用 specialp 的宏函数。这很重要,因为我们必须构建使用这些符号的代码。您不能使用函数来执行此操作,因为没有(可移植的)方法来获取符号并创建具有具有该名称的词法变量和引用它的 lambda 函数的词法环境。
如果您尝试将其与某些符号一起使用,这也存在一些风险。例如,在 SBCL 中,如果您尝试将例如 *standard-output* 绑定到不是流或流指示符的东西,您将收到错误:
CL-USER> (specialp *standard-output*)
; in: SPECIALP *STANDARD-OUTPUT*
; (LET ((*STANDARD-OUTPUT* 1))
; (LET ((#:FUNC-1038 (LAMBDA # *STANDARD-OUTPUT*)))
; (LET ((*STANDARD-OUTPUT* 2))
; (EQL 2 (FUNCALL #:FUNC-1038)))))
;
; caught WARNING:
; Constant 1 conflicts with its asserted type STREAM.
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
; caught 1 WARNING condition