我目前正在学习Common Lisp的范围属性。在我认为我有一个扎实的理解后,我决定编写一些我可以预测结果的例子,但显然我错了。我有三个问题,每个问题都与下面的例子有关:
示例1:
(defmethod fun1 (x)
(print x)
(fun2))
(defmethod fun2 ()
(print x))
(fun1 5)
输出:
5
*** - EVAL: variable X has no value
问题:这是有道理的。 x是静态范围的,fun2无法在没有明确传递的情况下找到x的值。
示例2:
(defvar x 100)
(defmethod fun1 (x)
(print x)
(fun2))
(defmethod fun2 ()
(print x))
(fun1 5)
输出:
5
5
问题:我不明白为什么x对fun2突然可见,其中fun1给出了值,而不是值为100 ...
示例3:
(setf x 100)
(defmethod fun1 (x)
(print x)
(fun2))
(defmethod fun2 ()
(print x))
(fun1 5)
输出:
5
100
问题:我是否应该忽略这些结果,因为在未声明的变量上调用setf显然是未定义的?这恰好是我在第二个例子中所期望的......
非常感谢任何见解......
答案 0 :(得分:18)
使用SETF设置未定义变量的效果在ANSI Common Lisp中未定义。
DEFVAR将定义一个特殊变量。此声明是全局的,也会对LET绑定产生影响。这就是按照惯例,这些变量写成*foo*
的原因。如果您曾经使用DEFVAR定义了X,那么它被声明为特殊的,并且以后无法将其声明为词汇。
默认情况下,LET提供本地词法变量。如果变量已经被声明为特殊(例如由于DEFVAR),那么它只是创建一个新的本地动态绑定。
<强>更新强>
没什么可看的。
X
已被宣布为特别。变量X的所有用法现在都使用动态绑定。
调用该函数时,将X绑定到5.动态。其他函数现在可以访问此动态绑定并获取该值。
这是Common Lisp中未定义的行为。您正在设置未声明的变量。然后发生的是依赖于实现的。您的实现(大多数做类似的事情)将X的符号值设置为100.在FUN1中,X是词法绑定的。在FUN2中,评估X检索X的符号值(或可能是动态绑定值)。
作为一个实现的例子,做了(做?)其他事情:CMUCL实现也默认在示例3中声明X是特殊的。设置一个未定义的变量也声明它是特殊的。
注意强>
在便携式标准兼容Common Lisp代码中,全局变量使用DEFVAR和DEFPARAMETER定义。两者都声明这些变量是特殊的。现在,这些变量的所有使用都涉及动态绑定。
记住:
((lambda (x)
(sin x))
10)
与
基本相同(let ((x 10))
(sin x))
这意味着LET绑定中的变量绑定和函数调用中的变量绑定的工作方式相同。如果X先前在某个地方被宣布为特殊,则两者都涉及动态绑定。
这在Common Lisp标准中指定。例如,请参阅SPECIAL declaration的解释。