处于非功能位置的符号被视为变量的名称。In (function variable)
function
在功能位置(在左括号后面),但variable
不在。除非明确引用的变量被其值替换。
如果要写的话(boundp my-variable)
,这意味着“是符号存储在变量值中,my-variable
绑定为变量”,而不是“符号存储my-variable
为变量。
那么为什么bound-and-truep
表现不同呢?
这是一个宏,常规(函数)求值规则在这里不适用,宏可以自由决定是否以及何时对它们的参数求值。宏实际上所做的是以某种方式转换参数并将结果作为列表返回,然后对其进行评估。转换和最终评估发生在不同的时间,称为宏扩展时间和评估时间。
这是定义的bound-and-true-p
样子:
(defmacro bound-and-true-p (var)
"Return the value of symbol VAR if it is bound, else nil."
`(and (boundp (quote ,var)) ,var))
这使用了与lisp宏不同的阅读器宏(更多内容请参见下文)。为了不使此问题进一步复杂化,请不要使用任何阅读器宏:
(defmacro bound-and-true-p (var)
"Return the value of symbol VAR if it is bound, else nil."
(list 'and (list 'boundp (list 'quote var)) var))
如果你写
(bound-and-true-p my-variable)
首先是“翻译”为
(and (boundp 'my-variable) my-variable)
然后评估返回的结果nil
是否my-variable
为boundp
,否则返回的值my-variable
(当然也可以是nil
)。
您可能已经注意到扩展不是
(and (boundp (quote my-variable)) my-variable)
如我们所料。quote
是一种特殊形式,而不是宏或函数。像宏一样,特殊形式可以对其参数做任何事情。这种特殊的特殊形式只是返回其参数(这里是一个符号),而不是该符号的变量值。这实际上是这种特殊形式的唯一目的:防止评估!宏无法自行执行此操作,因此需要使用宏quote
来执行此操作。
那怎么了'
?它是一个阅读器宏,如上所述,它与lisp宏不同。虽然宏用于转换代码/数据,但阅读器宏在读取文本时会更早使用,以便将文本转换为代码/数据。
'something
是的简写形式
(quote something)
`
实际定义中使用的bound-and-true-p
也是阅读器宏。如果它像那样引用一个符号,`symbol
则等价于'symbol
,但是当它被用来引用一个列表时,`(foo bar ,baz)
它在以前缀为形式的形式中表现不同,
。
`(constant ,variable)
相当于
(list (quote constant) variable))
这应该回答一个问题,为什么有时会评估(用其值替换)未引用的符号,而有时却不评估;宏可以quote
用来防止符号被评估。
但是为什么bound-and-true-p
宏boundp
却不是?我们必须能够确定是否将直到运行时才知道的任意符号绑定为符号。如果boundp
参数被自动引号,则不可能。
bound-and-true-p
用于确定是否定义了已知变量,如果已定义,则使用其值。如果库对第三方库具有可选的依赖关系,则这很有用:
(defun foo-get-value ()
(or (bound-and-true-p bar-value)
;; we have to calculate the value ourselves
(our own inefficient or otherwise undesirable variant)))
bound-and-true-p
可以被定义为一个函数并要求引用参数,但是因为它用于预先了解宏的变量是用来避免键入的情况'
。
setq
代表set quoted
,而原本是一个宏扩展到(set 'some-variable "less")
。通常,Elisp对于带引号和不带引号的参数并不一致,但是任何需要与变量而不是值交互的函数(不是宏)都将其引号引起来(这setq
是一个主要的例外)。