Common Lisp-CCL,为什么在将全局变量传递给局部函数时发出警告?

时间:2018-09-13 04:59:01

标签: lisp common-lisp ccl

我正在使用CCL学习Common Lisp。 我在本地使用全局变量时收到警告。为什么CCL提供此功能?这样做的目的是什么?

(setf n 75)

;;;This function works, but gets a warning about
;;;n being an undeclared free variable.
(defun use-global ()

(write n)
)

;;;This function works without warnings.
(defun global-to-local (x)

(write x)
)

(use-global)
(global-to-local n)

3 个答案:

答案 0 :(得分:3)

Setfsetq不会引入新变量,它们会修改现有变量。

为了定义类似全局变量的内容,请使用defvardefparameter。通常,它们以开头和结尾的*开头,并且会自动声明为 global special 。这意味着,只要您重新绑定它们,该绑定将对其上方的整个调用堆栈有效,无论您从那里调用什么功能,等等。

在您的示例中,顶级setf并没有这样做,因此在编译函数use-global时,编译器不会看到n的含义,并发出警告。在正确且惯用的Common Lisp代码中,通常应将此警告视为错误,因为它表示拼写错误或错字。

答案 1 :(得分:2)

该警告几乎没有价值。该变量受先前的顶级分配约束,该顶级分配在global environment中创建绑定,使该变量成为全局变量。它只是被访问,这很可能是程序员想要的。

当未看到变量定义时,未绑定变量警告非常有价值,因为它会捕获变量引用的拼写错误。但是,实现应注意顶层setfsetq并将其视为定义。

当变量由顶级setf定义,然后又受let约束时,发出警告是很有用的:

(setf n 42)

(let ((n 43)) (func))

在这里,程序员似乎期望n是一个特殊变量,但并不是那样定义的。 func会看到n的顶级绑定,该绑定包含42,而不是词汇表n的43绑定。因此,这里很有必要进行诊断。代码的目的是要求n已通过defvardefparameter声明,或被宣布为特殊。

(当然,在没有顶级(let ((n 43)) ...)的情况下,我们无法警告n,因为这是绝大多数词法变量的常见情况。)

在ANSI Common Lisp中有一个缺陷,即 3.1.2.1.1 Symbols as Forms 部分说只有三种变量:动态,词法和常量。动态变量是被声明为特殊变量,因此根据这种推理,setf不足以创建动态变量。但是,第3.1.1.1节明确指出存在全球环境。令人困惑的是,它说它包含具有不确定范围和范围的绑定,并且它包含动态变量。但是,然后在词汇表中将“动态变量”定义为在动态环境中具有绑定的变量,而将动态环境定义为包含具有“动态范围”的绑定。所以那不可能是全球环境。您知道3.1.1.1所说的包含动态变量!

无论如何,所有这些ANSI混淆都造成了误解,认为setfsetq无法创建变量,这为伪造的编译器诊断提供了支持。

答案 2 :(得分:0)

在Google Lisp中快速搜索意味着它是通过以下方式完成的:

(defvar *n* 75)

(defun use-global () (write *n*))

(use-global)

请注意按约定装饰全局名称的星号。