编辑:我在第一个答案后更改了示例代码,因为我想出了一个简单的版本来回避相同的问题。
我目前正在学习 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 显然是未定义的?这恰好是我在第二个示例中所期望的...
任何见解将不胜感激...
最佳答案
使用 setf
设置 undefined variable 的效果在 ANSI Common Lisp 中是未定义的。
defvar
将定义一个特殊变量。这个声明是全局的,也对 let
绑定(bind)有影响。这就是按照惯例将这些变量写成 *foo*
的原因。如果您曾经用 defvar
定义过 x
,它被声明为special 并且没有声明它的方法< em>词法 稍后。
let
默认提供本地词法变量。如果变量已经被声明为特殊变量(例如因为 defvar
),那么它只会创建一个新的本地动态绑定(bind)。
更新
- 示例 1。
没什么可看的。
- 示例 2
x
已被声明为特殊的。变量 x
的所有使用现在都使用动态绑定(bind)。
调用该函数时,将 x
绑定(bind)到 5
。动态地。其他函数现在可以访问此动态绑定(bind)并获取该值。
- 示例 3
这是 Common Lisp 中的未定义行为。您正在设置未声明的变量。然后会发生什么取决于实现。您的实现(大多数执行类似操作)将 x
的符号值设置为100
。在 fun1
中,x
是词法绑定(bind)的。在 fun2
中,评估 x
会检索 x
的符号值(或可能是动态绑定(bind)值)。
作为执行(执行?)其他操作的示例:默认情况下,CMUCL 实现还将示例 3 中的 x
声明为特殊的。设置一个 undefined variable 也会声明它是特殊的。
注意
在符合可移植标准的 Common Lisp 代码中,全局变量是用 defvar
和 defparameter
定义的。两者都声明这些变量是特殊的。这些变量的所有使用现在都涉及动态绑定(bind)。
记住:
((lambda (x)
(sin x))
10)
基本相同
(let ((x 10))
(sin x))
这意味着 let
绑定(bind)中的变量绑定(bind)和函数调用中的变量绑定(bind)以相同的方式工作。如果 x
早先在某个地方被声明为特殊的,那么两者都将涉及动态绑定(bind)。
这是在 Common Lisp 标准中指定的。例如参见对 SPECIAL declaration 的解释.
关于dynamic - Common Lisp 作用域(动态 vs 词法),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7787683/