Common Lisp 中的反引号:读取和评估
Backquote in Common Lisp: read and eval
这个问题在某种程度上与 this and 相关。基本上,如何阅读和评估反引号?正在发生什么过程?标准对此有任何说明吗?
这是我所期望的,但是它并没有发生:符号`是一个reader-宏并且被翻译成某种(BACKQUOTE ...)
macro/special 形式(类似于 '
被翻译成 (QUOTE ...)
)。这不会发生,事实上,Common Lisp 甚至没有 BACKQUOTE
宏。
发生了什么(SBCL):
CL-USER> (defparameter *q* (read-from-string "`(a b ,c)"))
*Q*
CL-USER> *q*
`(A B ,C)
CL-USER> (car *q*)
SB-INT:QUASIQUOTE
CL-USER> (cdr *q*)
((A B ,C))
与预期有些不同,但还可以。现在,,C
本身就是一个有趣的野兽:
CL-USER> (type-of (third (cadr *q*)))
SB-IMPL::COMMA
如果没有逗号,则可以评估读取的表达式:
CL-USER> (eval (read-from-string "`(a b c)"))
(A B C)
但是如果我想对 C
的本地绑定计算原始表达式,就会出现问题:
(let ((c 10)) (eval (read-from-string "`(a b ,c)")))
; in: LET ((C 10))
; (LET ((C 10))
; (EVAL (READ-FROM-STRING "`(a b ,c)")))
;
; caught STYLE-WARNING:
; The variable C is defined but never used.
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
; Evaluation aborted on #<UNBOUND-VARIABLE C {1007A3B2F3}>.
这意味着 EVAL
没有选择 C
绑定的环境。
PS。有趣的是,在 Elisp 中这是有效的。
反引号
Backquote is a standard macro character 在 Common Lisp 中。
在 Common Lisp 中,反引号表达式的表示是未定义的。实现实际上使用不同的表示。您看到的 SBCL 是特定于实现的。
评估
您遇到的 eval 问题与 reader 或反引号表达式完全无关:
? (let ((c 10))
(eval '(list 'a 'b c)))
Error: The variable C is unbound.
在 Common Lisp 中 EVAL
使用动态环境和 null lexical environment 来评估表单。以上词法环境,其中 c
绑定到 10
,未使用。
但是动态绑定是。我们需要将变量声明为 special
:
? (let ((c 10))
(declare (special c))
(eval '(list 'a 'b c)))
(A B 10)
因此这也有效:
? (let ((c 10))
(declare (special c))
(eval (read-from-string "`(a b ,c)")))
(A B 10)
Emacs Lisp had/has 默认动态绑定(尽管 GNU Emacs 现在也支持词法绑定)。 Common Lisp 默认有词法绑定。
这个问题在某种程度上与 this and
这是我所期望的,但是它并没有发生:符号`是一个reader-宏并且被翻译成某种(BACKQUOTE ...)
macro/special 形式(类似于 '
被翻译成 (QUOTE ...)
)。这不会发生,事实上,Common Lisp 甚至没有 BACKQUOTE
宏。
发生了什么(SBCL):
CL-USER> (defparameter *q* (read-from-string "`(a b ,c)"))
*Q*
CL-USER> *q*
`(A B ,C)
CL-USER> (car *q*)
SB-INT:QUASIQUOTE
CL-USER> (cdr *q*)
((A B ,C))
与预期有些不同,但还可以。现在,,C
本身就是一个有趣的野兽:
CL-USER> (type-of (third (cadr *q*)))
SB-IMPL::COMMA
如果没有逗号,则可以评估读取的表达式:
CL-USER> (eval (read-from-string "`(a b c)"))
(A B C)
但是如果我想对 C
的本地绑定计算原始表达式,就会出现问题:
(let ((c 10)) (eval (read-from-string "`(a b ,c)")))
; in: LET ((C 10))
; (LET ((C 10))
; (EVAL (READ-FROM-STRING "`(a b ,c)")))
;
; caught STYLE-WARNING:
; The variable C is defined but never used.
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
; Evaluation aborted on #<UNBOUND-VARIABLE C {1007A3B2F3}>.
这意味着 EVAL
没有选择 C
绑定的环境。
PS。有趣的是,在 Elisp 中这是有效的。
反引号
Backquote is a standard macro character 在 Common Lisp 中。
在 Common Lisp 中,反引号表达式的表示是未定义的。实现实际上使用不同的表示。您看到的 SBCL 是特定于实现的。
评估
您遇到的 eval 问题与 reader 或反引号表达式完全无关:
? (let ((c 10))
(eval '(list 'a 'b c)))
Error: The variable C is unbound.
在 Common Lisp 中 EVAL
使用动态环境和 null lexical environment 来评估表单。以上词法环境,其中 c
绑定到 10
,未使用。
但是动态绑定是。我们需要将变量声明为 special
:
? (let ((c 10))
(declare (special c))
(eval '(list 'a 'b c)))
(A B 10)
因此这也有效:
? (let ((c 10))
(declare (special c))
(eval (read-from-string "`(a b ,c)")))
(A B 10)
Emacs Lisp had/has 默认动态绑定(尽管 GNU Emacs 现在也支持词法绑定)。 Common Lisp 默认有词法绑定。