我知道当你想在 Lisp 中进行动态/全局绑定时,你可以使用 defparameter 或 defvar。我还知道,你可以使用 defun 参数列表或 let 语句来进行词法绑定,嗯,几乎无处不在。
我想知道的是,当我在代码中的其他任何地方没有声明或使用 x 这样的语句时,我到底做了什么:
(setf x 10 )
这似乎工作正常,并且 x 似乎不像词法变量。它实际上是一个动态的全局变量,就像我使用 defparameter 或 defvar 一样,还是完全不同?
我知道当你想在 Lisp 中进行动态/全局绑定时,你可以使用 defparameter 或 defvar。我还知道,你可以使用 defun 参数列表或 let 语句来进行词法绑定,嗯,几乎无处不在。
我想知道的是,当我在代码中的其他任何地方没有声明或使用 x 这样的语句时,我到底做了什么:
(setf x 10 )
这似乎工作正常,并且 x 似乎不像词法变量。它实际上是一个动态的全局变量,就像我使用 defparameter 或 defvar 一样,还是完全不同?
它的实际作用在 ANSI Common Lisp 标准中未指定。
一般来说,我更喜欢任何 CL 实现来设置动态绑定值或全局值。它不应该做任何其他事情。默认情况下,CMUCL 似乎认为将符号声明为特殊是个好主意。但这是个坏主意,因为没有明显的方法可以摆脱全球特别声明。
所以,通常我会期待这样的事情(这里是 LispWorks):
CL-USER 66 > (defun foo () (setf x44 10))
FOO
全局变量仍然未绑定:
CL-USER 67 > x44
Error: The variable X44 is unbound.
1 (continue) Try evaluating X44 again.
2 Specify a value to use this time instead of evaluating X44.
3 Specify a value to set X44 to.
4 (abort) Return to level 0.
5 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 68 : 1 > :top
让我们调用函数:
CL-USER 69 > (foo)
10
现在它具有全局价值:
CL-USER 70 > x44
10
但是变量没有被声明为特殊的(因为它会被DEFVAR
or DEFPARAMETER
)。这里建立了一个词法绑定。
CL-USER 71 > (let ((x44 20)) (foo) x44)
20
当我们将局部变量声明为特殊时,我们的函数会更改绑定:
CL-USER 72 > (let ((x44 20)) (declare (special x44)) (foo) x44)
10
很快,您可以想到setq
,这是setf
您观察到的扩展,只做了一半的事情defvar
或defparameter
做:
考虑这样defparameter
做:
(declaim (special x))
(setq x 10)
即它既向x
编译器提供了一些元数据(关于什么类型的数据)(在这种情况下,它告诉它它是一个“特殊”变量)并赋值。
特别是defvar
,如果它是顶级表单,则不会这样。标准行为是只初始化符号的值单元一次,因此其代码将更加复杂,您可以将其视为:
(unless (boundp x) ; This is not entirely correct, because if the symbol
; is otherwise known to the environment, but is unbound
; defvar will not re-bind it, but I can't think of a way
; to mimic that behavior
(declaim (special x))
(setq x 10))
提供给编译器的元数据可能会对代码的行为方式产生任何影响,也可能不会。一般来说,元数据应该帮助编译器更好地判断代码背后的意图,从而可能导致优化。但它也可用于文档或调试。
据我所知,您应该避免将 setf(扩展为 setq)与未声明的变量一起使用。实现之间的行为可能会有所不同,即使您在 REPL 中的代码运行良好,编译的程序也可能在意想不到的地方出现错误。如果您执行以下操作,您可以在 clisp 中看到警告:
>(defun internal-setf () (setf some-var 10))
>(compile 'internal-setf)
WARNING: in INTERNAL-SETF : SOME-VAR is neither declared nor bound,
it will be treated as if it were declared SPECIAL.