12

编辑:我在第一个答案后更改了示例代码,因为我想出了一个简单的版本,它提出了同样的问题。

我目前正在学习 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 突然以 fun1 给它的值对 fun2 可见,而不是值为 100...

示例 3:

(setf x 100)

(defmethod fun1 (x)
  (print x)
  (fun2))

(defmethod fun2 ()
  (print x))

(fun1 5)

输出:

5
100

问题:我是否应该忽略这些结果,因为在未声明的变量上调用 setf 显然是未定义的?这恰好是我在第二个示例中所期望的......

任何见解将不胜感激......

4

1 回答 1

23

setf在 ANSI Common Lisp中设置未定义变量 using 的效果是未定义的。

defvar将定义一个特殊的变量let此声明是全局的,并且对绑定也有影响。这就是按照惯例将这些变量写为的原​​因*foo*。如果你曾经用 定义xdefvar,它会被声明为特殊的,并且以后无法声明它是词法的。

let默认情况下提供局部词法变量。如果变量已经被声明为特殊的(例如因为 a defvar),那么它只会创建一个新的本地动态绑定。

更新

  • 示例 1。

没什么可看的。

  • 示例 2

x已被宣布为特殊。变量的所有使用x现在都使用动态绑定。调用该函数时,您绑定x5. 动态地。其他函数现在可以访问此动态绑定并获取该值。

  • 示例 3

这是Common Lisp 中未定义的行为。您正在设置一个未声明的变量。然后发生的事情取决于实现。您的实现(大多数做类似的事情)将符号值设置为xto 100。在fun1,x是词法绑定的。在fun2评估x中检索 的符号值(或可能是动态绑定值)x

作为一个实现(做了?)其他事情的示例:CMUCL 实现也将x在示例 3 中默认声明为特殊的。设置一个未定义的变量也会声明它是特殊的。

笔记

在符合可移植标准的 Common Lisp 代码中,全局变量用defvar和定义defparameter。两者都声明这些变量是特殊的。这些变量的所有使用现在都涉及动态绑定。

记住:

((lambda (x)
   (sin x))
 10)

基本上是一样的

(let ((x 10))
  (sin x))

这意味着绑定中的let变量绑定和函数调用中的变量绑定以相同的方式工作。如果x之前在某个地方被声明为特殊的,那么两者都将涉及动态绑定。

这是在 Common Lisp 标准中指定的。参见例如对SPECIAL 声明的解释。

于 2011-10-17T01:18:30.307 回答