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)
请注意按照惯例修饰全局名称的星号。
Setf
和setq
不引入新变量,它们修改现有变量。
为了定义诸如全局变量之类的东西,请使用defvar
或defparameter
。这些通常以开头和结尾 *
编写,并且自动声明为 全局特殊 。这意味着无论何时您重新绑定它们,此绑定都会对其上方的整个调用堆栈生效,无论您从那里调用什么函数等等。
在您的示例中,顶层 setf
没有这样做,因此在编译函数 use-global
时,编译器看不到 n
的意思并发出警告。在正确且惯用的 Common Lisp 代码中,此警告通常应被视为错误,因为它表示拼写错误或拼写错误。
该警告几乎没有价值。该变量由先前的顶级赋值绑定,该赋值在 global environment 中创建绑定,使该变量成为全局变量。它只是被访问,这可能是程序员的意图。
当没有看到变量的定义时,未绑定变量警告非常有价值,因为它会捕获变量引用的拼写错误。但是顶级 setf
或 setq
应该被实现注意到并被视为定义。
当变量由顶级 setf
定义,然后又受 let
:
定义时,发出警告很有用
(setf n 42)
(let ((n 43)) (func))
在这里,看起来程序员可能期望 n
是一个特殊变量,但它并没有那样定义。 func
将看到包含 42 的 n
的顶级绑定,而不是包含 43 的词法 n
。因此有充分的理由在这里进行诊断。代码的意图要求 n
已通过 defvar
或 defparameter
声明,或声明为特殊的。
(当然,我们不能在没有看到顶级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 混淆造成了 setf
或 setq
无法创建变量的误解,这为伪造的编译器诊断提供了支持。
我正在使用 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)
请注意按照惯例修饰全局名称的星号。
Setf
和setq
不引入新变量,它们修改现有变量。
为了定义诸如全局变量之类的东西,请使用defvar
或defparameter
。这些通常以开头和结尾 *
编写,并且自动声明为 全局特殊 。这意味着无论何时您重新绑定它们,此绑定都会对其上方的整个调用堆栈生效,无论您从那里调用什么函数等等。
在您的示例中,顶层 setf
没有这样做,因此在编译函数 use-global
时,编译器看不到 n
的意思并发出警告。在正确且惯用的 Common Lisp 代码中,此警告通常应被视为错误,因为它表示拼写错误或拼写错误。
该警告几乎没有价值。该变量由先前的顶级赋值绑定,该赋值在 global environment 中创建绑定,使该变量成为全局变量。它只是被访问,这可能是程序员的意图。
当没有看到变量的定义时,未绑定变量警告非常有价值,因为它会捕获变量引用的拼写错误。但是顶级 setf
或 setq
应该被实现注意到并被视为定义。
当变量由顶级 setf
定义,然后又受 let
:
(setf n 42)
(let ((n 43)) (func))
在这里,看起来程序员可能期望 n
是一个特殊变量,但它并没有那样定义。 func
将看到包含 42 的 n
的顶级绑定,而不是包含 43 的词法 n
。因此有充分的理由在这里进行诊断。代码的意图要求 n
已通过 defvar
或 defparameter
声明,或声明为特殊的。
(当然,我们不能在没有看到顶级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 混淆造成了 setf
或 setq
无法创建变量的误解,这为伪造的编译器诊断提供了支持。