【发布时间】:2011-09-22 10:02:51
【问题描述】:
我在使用 Lisp 的反引号读取宏时遇到问题。每当我尝试编写一个似乎需要使用嵌入式反引号的宏时(例如,来自 Paul Graham 的 ANSI Common Lisp,第 399 页的``(w ,x ,,y)),我无法弄清楚如何编写我的代码一种编译方式。通常,我的代码会收到一整串以“逗号不在反引号内”开头的错误。有人可以就我如何编写能够正确评估的代码提供一些指导吗?
例如,我目前需要一个宏,它采用以'(function-name column-index value) 的形式描述规则并生成谓词 lambda 主体以确定特定行的 column-index 索引的元素是否满足规则.如果我使用规则 '(< 1 2) 调用此宏,我希望生成一个如下所示的 lambda 主体:
(lambda (row)
(< (svref row 1) 2))
我能做出的最好的尝试如下:
(defmacro row-satisfies-rule (rule)
(let ((x (gensym)))
`(let ((,x ,rule))
(lambda (row)
(`,(car ,x) (svref row `,(cadr ,x)) `,(caddr ,x))))))
在评估时,SBCL 会发出以下错误报告:
; in: ROW-SATISFIES-RULE '(< 1 2)
; ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121))
;
; caught ERROR:
; illegal function call
; (LAMBDA (ROW) ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121)))
; ==>
; #'(LAMBDA (ROW) ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121)))
;
; caught STYLE-WARNING:
; The variable ROW is defined but never used.
; (LET ((#:G1121 '(< 1 2)))
; (LAMBDA (ROW) ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121))))
;
; caught STYLE-WARNING:
; The variable #:G1121 is defined but never used.
;
; compilation unit finished
; caught 1 ERROR condition
; caught 2 STYLE-WARNING conditions
#<FUNCTION (LAMBDA (ROW)) {2497F245}>
如何编写宏来生成我需要的代码,特别是如何实现row-satisfies-rule?
使用 Ivijay 和 discipulus 的想法,我修改了宏,使其能够编译和工作,甚至允许将表单作为参数传递。它的运行方式与我最初计划的宏有点不同,因为我确定将 row 作为参数包含在内以使代码更流畅。然而,它像罪恶一样丑陋。有谁知道如何清理它,以便在不调用 eval 的情况下执行相同的操作?
(defmacro row-satisfies-rule-p (row rule)
(let ((x (gensym))
(y (gensym)))
`(let ((,x ,row)
(,y ,rule))
(destructuring-bind (a b c) ,y
(eval `(,a (svref ,,x ,b) ,c))))))
此外,非常感谢解释干净的 Lispy 方法以获取宏以生成代码以在运行时正确评估参数。
【问题讨论】:
-
gensyms 真的有必要吗?您没有在扩展中引入任何新变量,并且用于生成扩展的变量名称不会导致泄漏。 -
去掉
gensyms,去掉eval,然后就是三行了,好看,还行。你甚至可以把destructuring-bind去掉,变成两行。 -
值得注意的是,事实上,宏甚至不是必需的;事实上,一个函数可能更合适。
标签: macros lisp common-lisp backticks