为什么 (let ((x 'huh?)) (cons (boundp 'x) x)) 的计算结果为 (NIL . HUH?)?

kjo*_*kjo 4 common-lisp

我不明白这个:

CL-USER> (let ((x 'huh?)) (cons (boundp 'x) x))
(NIL . HUH?)
Run Code Online (Sandbox Code Playgroud)

我预计在let上面的表达式中,x将被绑定,因此整个表达式将被计算为(t . huh?)。否则,如果(与我的期望相反)x没有绑定在let的主体中,那么至少上面表达式的求值会导致错误(因为我将一个未绑定的变量作为第二个参数传递给cons)。

更让我困惑的是,Common Lisp HyperSpec 的描述是boundp 这样的

如果符号已绑定,则返回 true;否则,返回 false。

...其中“bound”一词超链接到该术语表定义(我的重点)1

bound adj.,vt 1. adj. 束缚的。在绑定中具有关联的表示。 “let 命名的变量绑定在其主体内。”请参阅 unbound。2. adj. 形容词 具有遮蔽[2]另一个的本地绑定。“变量print-escape在 princ 函数中被绑定。” 3. vt 绑定的过去时。

此外,CLHS 的文档说明let了以下内容(我的重点):

...所有变量varj绑定到相应的值;...

当然,HyperSpec 的页面boundp(我之前已经链接到了)有以下示例:

(let ((x 2)) (boundp 'x)) =>  false
Run Code Online (Sandbox Code Playgroud)

...这确实证明了我所观察到的实际上是“官方记录的行为”这一断言,但鉴于我上面引用的其他所有内容,这种狭隘正确的理由并不能让人感到安慰。

有人可以帮我解决这个巨大的(希望只是表面上的)矛盾吗?


1我意识到上面突出显示的短语只是如何在句子中使用“bound”一词的示例,但如果它所表述的内容与 Common 的实际情况完全相反,那么这将是一个真正反常的示例口齿不清。

ex *_*ilo 5

boundp is for determining whether symbols are bound in the global environment. Note the following two examples from the HyperSpec:

(let ((x 2)) (boundp 'x)) ;=>  false  
(let ((x 2)) (declare (special x)) (boundp 'x)) ;=>  true
Run Code Online (Sandbox Code Playgroud)

The notes at the bottom of the page say:

The function bound determines only whether a symbol has a value in the global environment; any lexical bindings are ignored.

The appearance of bound in the note instead of boundp seems to be a typo. In any case, CLTL2 was a bit more specific about this:

boundp is true if the dynamic (special) variable named by symbol has a value; otherwise, it returns nil.

Note that fboundp has a similar restriction; here is an example from the HyperSpec:

(flet ((my-function (x) x))  
  (fboundp 'my-function)) ;=>  false
Run Code Online (Sandbox Code Playgroud)

There isn't much point in boundp handling lexical variables. From the HyperSpec 3.1.2.1.1.1 Lexical Variables:

A lexical variable always has a value. There is no operator that introduces a binding for a lexical variable without giving it an initial value, nor is there any operator that can make a lexical variable be unbound.

This is to say that lexical variables are always bound in their lexical environments. But dynamic variables may be either bound or unbound, and which of the two is the case can depend upon the environment in which the question is asked:

The value part of the binding for a dynamic variable might be empty; in this case, the dynamic variable is said to have no value, or to be unbound. A dynamic variable can be made unbound by using makunbound....

A dynamic variable is unbound unless and until explicitly assigned a value, except for those variables whose initial value is defined in this specification or by an implementation.


cor*_*ump 5

This can be a bit confusing, the spec for BOUNDP does however say that:

函数bound [sic(应该是boundp)]仅确定符号在全局环境中是否有值;任何词汇绑定都会被忽略

因此,它仅通知您给定符号是否绑定在全局环境中,如果变量将其值单元格设置为某个值(请参阅 参考资料SYMBOL-VALUE),或者该变量被声明为特殊变量并且之前已由let表单绑定,则会发生这种情况。第二种情况尤其发生在使用defvarand声明的变量上defparameter,而且也发生在您声明为特殊的任何变量上:

(let ((%my-var% 0))
  (declare (special %my-var%))
  ...)
Run Code Online (Sandbox Code Playgroud)

请注意,每次您想要使用时,%my-var%您都需要使用该声明,除非您在全局范围内声明它。

(defun use-my-var (input)
  (declare (special %my-var%))
  (print `(:my-var ,%my-var% :input ,input)))
Run Code Online (Sandbox Code Playgroud)

当您编写use-my-var函数时,通常可以毫无问题地识别input绑定,事实上,如果情况并非如此,编译器会警告您。对于词法作用域,(boundp x)将编译为常量值 T 或 NIL。symbol-value检查符号的 是全局绑定还是动态绑定更有趣。

上面,由于%my-var%是一个特殊变量,因此它可以在不同的调用上下文中绑定或不绑定:

(let ((%my-var% 0))
  (declare (special %my-var%))
  (use-my-var 1))
=> (:my-var 0 :input 1)

(use-my-var 0)
;; ERROR: The variable %MY-VAR% is unbound.
Run Code Online (Sandbox Code Playgroud)