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

Common Lisp - CCL, why a warning when passing a global to a local function?

我正在使用 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)

在 Common Lisp 中快速谷歌搜索表明它是这样完成的:

(defvar *n* 75)

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

(use-global)

请注意按照惯例修饰全局名称的星号。

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

为了定义诸如全局变量之类的东西,请使用defvardefparameter。这些通常以开头和结尾 * 编写,并且自动声明为 全局特殊 。这意味着无论何时您重新绑定它们,此绑定都会对其上方的整个调用堆栈生效,无论您从那里调用什么函数等等。

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

该警告几乎没有价值。该变量由先前的顶级赋值绑定,该赋值在 global environment 中创建绑定,使该变量成为全局变量。它只是被访问,这可能是程序员的意图。

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

当变量由顶级 setf 定义,然后又受 let:

定义时,发出警告很有用
(setf n 42)

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

在这里,看起来程序员可能期望 n 是一个特殊变量,但它并没有那样定义。 func 将看到包含 42 的 n 的顶级绑定,而不是包含 43 的词法 n。因此有充分的理由在这里进行诊断。代码的意图要求 n 已通过 defvardefparameter 声明,或声明为特殊的。

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

ANSI Common Lisp 有一点缺陷,3.1.2.1.1 Symbols as Forms says that there are only three kinds of variables: dynamic, lexical and constant. A dynamic variable is one that is declared special, and so according to this reasoning, setf is not enough to create a dynamic variable. However, section 3.1.1.1 部分清楚地说明了存在全局环境。令人困惑的是,它说它包含具有无限范围和范围的绑定,并且它包含动态变量。但是 "dynamic variable" 在词汇表中被定义为在动态环境中具有绑定的变量,而动态环境被定义为包含具有 "dynamic extent" 的绑定。所以那不可能是全球环境。你知道吗,3.1.1.1里面说的那个是动态变量!

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