1

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

3 回答 3

3

Setf并且setq不引入新变量,它们会修改现有变量。

为了定义像全局变量这样的东西,使用defvaror defparameter。这些通常以开头和结尾编写*,并且它们被自动声明为全局特殊的。这意味着每当您重新绑定它们时,此绑定对其上方的整个调用堆栈都有效,无论您从那里调用的任何函数等。

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

于 2018-09-13T05:41:30.043 回答
2

该警告提供的价值不大。该变量由先前的顶级赋值绑定,该赋值在全局环境中创建一个绑定,使该变量成为全局变量。它只是被访问,这可能是程序员的意图。

当没有看到变量的定义时,未绑定变量警告非常有价值,因为它会捕获变量引用的拼写错误。但是一个顶层setf还是setq应该被实现所注意并被当作一个定义来对待。

当变量由顶级定义setf,然后受制于以下情况时,发出警告很有用let

(setf n 42)

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

在这里,看起来程序员可能期望n成为一个特殊变量,但它不是这样定义的。func将看到拥有 42 的顶级绑定n,而不是n拥有 43 的词汇。所以这里有充分的理由进行诊断。代码的意图需要n通过defvaror声明defparameter,或声明为特殊。

(当然,我们不能警告什么(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不能创建变量,这为伪造的编译器诊断提供了支持。

于 2018-09-14T03:43:38.317 回答
0

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

(defvar *n* 75)

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

(use-global)

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

于 2018-09-13T05:12:08.743 回答