Common Lisp范围(动态与词汇)

Ant*_*deo 10 lisp dynamic common-lisp scoping lexical

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

我目前正在学习Common Lisp的范围属性.在我认为我有一个扎实的理解后,我决定编写一些我可以预测结果的例子,但显然我错了.我有三个问题,每个问题都与下面的例子有关:

例1:

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

(defmethod fun2 ()
  (print x))

(fun1 5)
Run Code Online (Sandbox Code Playgroud)

输出:

5 
*** - EVAL: variable X has no value
Run Code Online (Sandbox Code Playgroud)

问题:这是有道理的.x是静态范围的,fun2无法在没有显式传递的情况下找到x的值.

例2:

(defvar x 100)

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

(defmethod fun2 ()
  (print x))

(fun1 5)
Run Code Online (Sandbox Code Playgroud)

输出:

5
5
Run Code Online (Sandbox Code Playgroud)

问题:我不明白为什么x对fun2突然可见,其值为fun1,而不是值为100 ...

例3:

(setf x 100)

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

(defmethod fun2 ()
  (print x))

(fun1 5)
Run Code Online (Sandbox Code Playgroud)

输出:

5
100
Run Code Online (Sandbox Code Playgroud)

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

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

Rai*_*wig 18

使用SETF设置未定义变量的效果在ANSI Common Lisp中是未定义的.

DEFVAR将定义一个特殊变量.此声明是全局的,也会对LET绑定产生影响.这就是按照惯例,这些变量被写为*foo*.如果您曾经使用DEFVAR定义了X,那么它将被声明为特殊的,并且以后无法将其声明为词法.

LET默认提供本地词法变量.如果变量已经被声明为特殊(例如由于DEFVAR),那么它只是创建一个新的本地动态绑定.

更新

  • 例1.

没什么好看的.

  • 例2

X已被宣布为特别.变量X的所有用法现在都使用动态绑定.调用该函数时,将X绑定到5.动态.其他函数现在可以访问此动态绑定并获取该值.

  • 例3

这是Common Lisp中未定义的行为.您正在设置未声明的变量.然后发生的是依赖于实现的.您的实现(大多数做类似的事情)将X的符号值设置为100.在FUN1中,X是词法绑定的.在FUN2中,评估X检索X的符号值(或可能是动态绑定值).

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

注意

在便携式标准兼容Common Lisp代码中,全局变量使用DEFVAR和DEFPARAMETER定义.两者都声明这些变量是特殊的.现在,这些变量的所有使用都涉及动态绑定.

记得:

((lambda (x)
   (sin x))
 10)
Run Code Online (Sandbox Code Playgroud)

基本上是一样的

(let ((x 10))
  (sin x))
Run Code Online (Sandbox Code Playgroud)

这意味着LET绑定中的变量绑定和函数调用中的变量绑定的工作方式相同.如果X先前在某个地方被声明为特殊,则两者都涉及动态绑定.

这在Common Lisp标准中指定.例如,参见SPECIAL声明的解释.