使用 "setf" 创建特殊变量是否有效?

Is it valid to use "setf" to create special variables?

看来我可以使用 setf 创建特殊变量。例如,如果我启动 REPL 并输入 (setf x 123),则会创建一个特殊变量 x。 CLISP 和 ECL 没有错误。 SBCL 发出警告 (undefined variable: COMMON-LISP-USER::X),但无论如何都会创建特殊变量。 setf 是创建特殊变量的有效方法吗?

使用setf创建新变量无效。 HyperSpec 很清楚 setfonly intended to update existing variables:

setf changes the value of place to be newvalue.

(setf place newvalue) expands into an update form that stores the result of evaluating newvalue into the location referred to by place.

setf 用于更新地点。没有为尝试使用 setf 更新尚不存在的地点的值指定行为。

在讨论赋值的部分,CLTL2说得更多:

Such alteration is different from establishing a new binding. Constructs for establishing new bindings of variables are described in section 7.5.

虽然这看起来可行,但您不能依赖它。它通常在 REPL 中有效,如果你想在 REPL 中使用这种方式 setf 很好,但你不应该在任何程序中这样做。

其实有点棘手

这是 Common Lisp 中未指定的内容之一。不幸的是。

LispWorks:

CL-USER 61 > (setf foo 1)
1

CL-USER 62 > (defun bar () foo)
BAR

CL-USER 63 > (bar)
1

CL-USER 64 > (let ((foo 2))
               (bar))
1

let 中的最后一个 foo 使用词法绑定,因此不认为它是特殊的。

在形式 (setf foo 1)(defun bar () foo) 中,变量 foo 被特定的 Lisp 实现 假定 是特殊的,它甚至可以被 setf 声明为特殊的(-> 大多数实现都没有)。

以上let形式returns1还是2在Common Lisp语言标准中未指定。不过,大多数实现将 return 1

下一个:

CL-USER 65 > (let ((foo 2))
               (declare (special foo))
               (bar))
2

上面我们看到在bar里面使用foo其实就是在let.

里面使用了foo的动态绑定

基本上,设置未定义变量的确切效果是未指定的。可以假定该变量是否特殊。大多数实现更喜欢 NOT declare it special,这样变量的进一步使用必须是特殊的。

但是变量是否特殊,以及使用它的确切效果,在Common Lisp语言标准中实际上是未定义的。

大多数实施都同意

  1. setf设置一个未定义的变量只会改变符号的符号值

  2. 设置和检索变量假设一个特殊变量

  3. 通过let(或类似)重新绑定变量不会创建一个特殊变量

  4. 编译器(如果使用)将警告未定义的变量and/or关于假定特殊变量。

默认不同意的一个实现是 CMUCL -> setf 也将变量声明为特殊变量,类似于 defparameter 所做的。

有关一般样式规则,请参阅 荒谬 的回答的最后一段。