Common lisp 在LISP中使用普通setf语句时会发生什么?

Common lisp 在LISP中使用普通setf语句时会发生什么?,common-lisp,setf,Common Lisp,Setf,我知道,当您想在Lisp中进行动态/全局绑定时,可以使用defparmeter或defvar。我还知道,几乎在任何地方都可以使用defun参数列表或let语句进行词汇绑定 我想知道的是,当我在代码中的其他地方没有声明或使用x时,我到底是怎么做的: (setf x 10 ) 这似乎工作正常,x的行为似乎不像一个词汇变量。它实际上是一个动态的全局变量,就像我使用了defparameter或defvar一样,还是完全其他的东西?很快,你就会想到,这是你观察到的setf的扩展,只做了defvar或de

我知道,当您想在Lisp中进行动态/全局绑定时,可以使用defparmeter或defvar。我还知道,几乎在任何地方都可以使用defun参数列表或let语句进行词汇绑定

我想知道的是,当我在代码中的其他地方没有声明或使用x时,我到底是怎么做的:

(setf x 10 )

这似乎工作正常,x的行为似乎不像一个词汇变量。它实际上是一个动态的全局变量,就像我使用了defparameter或defvar一样,还是完全其他的东西?

很快,你就会想到,这是你观察到的
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))
提供给编译器的元数据可能会也可能不会对代码的行为产生任何影响。一般来说,元数据应该帮助编译器更好地判断代码背后的意图,从而可能导致优化。但它也可以用于文档或调试


您可以在Hyperspec中阅读和。

据我所知,您应该避免对未声明的变量使用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.

它的实际功能在ANSI通用Lisp标准中没有明确说明

一般来说,我更喜欢任何CL实现只设置动态绑定值或全局值。它不应该做任何其他事情。默认情况下,CMUCL似乎认为宣布符号为特殊符号是一个好主意。但这是一个坏主意,因为没有明显的办法摆脱全球特别宣言

因此,通常我会期望这样的结果(这里是LispWorks):

全局变量仍处于未绑定状态:

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 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

谢谢你的回答真的很有帮助。使用您的答案,再加上REPL中的一些测试,最终帮助我完全理解了我原始陈述中可能出现的行为。现在我也知道了特殊变量和非特殊变量之间的区别。
CL-USER 71 > (let ((x44 20)) (foo) x44)
20
CL-USER 72 > (let ((x44 20)) (declare (special x44)) (foo) x44)
10