6

我知道当你想在 Lisp 中进行动态/全局绑定时,你可以使用 defparameter 或 defvar。我还知道,你可以使用 defun 参数列表或 let 语句来进行词法绑定,嗯,几乎无处不在。

我想知道的是,当我在代码中的其他任何地方没有声明或使用 x 这样的语句时,我到底做了什么:

(setf x 10 )

这似乎工作正常,并且 x 似乎不像词法变量。它实际上是一个动态的全局变量,就像我使用 defparameter 或 defvar 一样,还是完全不同?

4

3 回答 3

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

但是变量没有被声明为特殊的(因为它会被DEFVARor DEFPARAMETER)。这里建立了一个词法绑定。

CL-USER 71 > (let ((x44 20)) (foo) x44)
20

当我们将局部变量声明为特殊时,我们的函数会更改绑定:

CL-USER 72 > (let ((x44 20)) (declare (special x44)) (foo) x44)
10
于 2012-12-26T13:06:39.763 回答
2

很快,您可以想到setq,这是setf您观察到的扩展,只做了一半的事情defvardefparameter做:

考虑这样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 的special信息。declare

于 2012-12-26T10:33:51.270 回答
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.
于 2012-12-26T10:38:20.660 回答