我知道当你想在Lisp中创建一个动态/全局绑定时,你可以使用defparameter或defvar。我也知道你可以使用defun参数列表或let语句在几乎所有地方进行词法绑定。
我想知道的是,当我在这样的声明中声明x未被声明或在代码中的任何其他地方使用时,我究竟是在做什么:
(setf x 10 )
这似乎工作正常,x似乎不像词法变量。它实际上是一个动态的全局,就像我使用defparameter或defvar一样,还是完全不同于其他东西?
答案 0 :(得分:6)
它的实际功能在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
或DEFPARAMETER
)。这里建立了一个词汇绑定。
CL-USER 71 > (let ((x44 20)) (foo) x44)
20
当我们声明局部变量是特殊的时,我们的函数会改变绑定:
CL-USER 72 > (let ((x44 20)) (declare (special x44)) (foo) x44)
10
答案 1 :(得分:2)
很快,你可以想到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))
的行为不会如此。标准行为是仅将符号的值单元初始化一次,因此其代码将更加复杂,您可以将其视为:
{{1}}
提供给编译器的元数据可能会或可能不会对代码的行为产生任何影响。通常,元数据应该有助于编译器更好地判断代码背后的意图,从而可能导致优化。但它对文档或调试也很有用。
答案 2 :(得分:0)
据我所知,你应该避免使用未声明变量的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.