我对符号和全局变量之间的关系有疑问。
超标准表示符号的value属性:
“如果符号具有值属性,则称其为绑定,并且该事实可以由函数boundp检测。绑定符号的值单元格中包含的对象是由以下命名的全局变量的值:该符号,可以通过函数symbol-value访问。“
如果我执行以下步骤:
CL-USER> (intern "*X*")
*X*
NIL
CL-USER> (boundp '*x*)
NIL
CL-USER> (setf (symbol-value '*x*) 1)
1
CL-USER> (boundp '*x*)
T
据我了解,上述条件已得到满足。应该有一个由符号命名的全局变量,变量的值是符号值。但这是错误的。
CL-USER> (describe '*x*)
COMMON-LISP-USER::*X*
[symbol]
*X* names an undefined variable:
Value: 1
; No value
CL-USER>
必须宣布特别。
CL-USER> (proclaim '(special *x*))
; No value
CL-USER> (describe '*x*)
COMMON-LISP-USER::*X*
[symbol]
*X* names a special variable:
Value: 1
; No value
CL-USER>
你能解释一下这种行为吗?什么意思是“未定义的变量”,我没有在超标准中找到这个术语。
(我使用SBCL 1.3.15。)
感谢您的回答。
编辑:
(由于此评论适用于以下两个答案(用户Svante和coredump),我将其写为编辑,而不是对两个答案的评论)。
我同意* x *是全局变量的答案。
全局变量的超标准状态:
“全局变量,动态变量或常量变量。”
因此我认为,现在,SBCL说“未定义”的原因不在于它是否特殊,而在于它是动态(特殊)变量还是常量变量(hyperspec:“常量变量n。变量,价值永远不会改变“)。
以下答案中提到的第三个定义(也许我理解错误的答案),根据超级规范,它是一个不特殊的全局变量(而不是常量)。
你能同意吗?
编辑2:
好的,总的来说,我认为,由于超级规范没有定义未定义的全局变量,因此它们不存在。
但正确的答案是,它们确实存在并且未定义,这意味着它依赖于实现,如何处理它们。
感谢您的回答,我接受了他们三个,但我只能标记一个。
答案 0 :(得分:4)
是由符号命名的全局变量,其值是符号值。这就是输出告诉你的。 undefined 的东西是变量的状态:它是否特殊。我同意输出的措辞有点特殊。
如果你设置一个变量的值而不先创建它(这也是裸setq
也会这样做的话),那么它是否变得特殊是未定义的。
传统上,人们不使用非特殊的全局变量。这就是为什么你应该使用defvar
,defparameter
等等。
答案 1 :(得分:4)
如其他答案中所述,*X*
未声明特殊(动态)。如果你的词汇绑定符号,SBCL也会给你一个警告:
FUN> (let ((*X* 30)) (list *X* (symbol-value '*X*)))
; in: LET ((*X* 30))
; (LET ((FUN::*X* 30))
; (LIST FUN::*X* (SYMBOL-VALUE 'FUN::*X*)))
;
; caught STYLE-WARNING:
; using the lexical binding of the symbol (FUN::*X*), not the
; dynamic binding, even though the name follows
; the usual naming convention (names like *FOO*) for special variables
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
(30 10)
另请注意,如果*X*
在本地声明为特殊情况,会发生什么:
FUN> (let ((*X* 30)) (declare (special *X*)) (list *X* (symbol-value '*X*)))
(30 30)
symbol-value
访问器从动态环境中检索绑定。
答案 2 :(得分:3)
根据超级规范,不存在非特殊(而非常数)的全局变量。
标准中未定义实际行为,但在实现中它可能以某种方式工作。
LispWorks中的这个例子:
CL-USER 46 > (boundp 'foo)
NIL
所以FOO
是未绑定的。
CL-USER 47 > (defun baz (bar) (* foo bar))
BAZ
上面在LispWorks 解释器中定义了一个函数baz
- 它没有被编译。没有警告。
现在我们设置此符号foo
:
CL-USER 48 > (setq foo 20)
20
CL-USER 49 > (baz 22)
440
我们已成功调用它,即使FOO
未被声明为全局函数。
让我们检查,如果它被声明为特殊:
CL-USER 50 > (SYSTEM:DECLARED-SPECIAL-P 'foo)
NIL
现在我们从上面编译函数:
CL-USER 51 > (compile 'baz)
;;;*** Warning in BAZ: FOO assumed special
BAZ
编译器说它不知道FOO
并假设它是特殊的。
此行为未定义且实现不同:
解释器可能只使用全局符号值而不是抱怨 - 请参阅上面的LispWorks示例。这在实施中相对常见。
编译器可能会假定未定义的变量是一个特殊变量并发出警告。这在实现中也相对常见。
编译器可能会假定未定义的变量是一个特殊变量,并且声明该变量是特殊的。这不常见 - 默认情况下CMUCL(确实如此)。此行为并不常见且不受欢迎,因为没有标准方法来撤消声明。