sad*_*tsu 9 lisp macros common-lisp backticks
我在使用Lisp的反引用读取宏时遇到问题.每当我尝试编写一个似乎需要使用嵌入式反引号的宏时(例如,``(w ,x ,,y)来自Paul Graham的ANSI Common Lisp,第399页),我无法弄清楚如何以编译的方式编写代码.通常情况下,我的代码会收到一系列错误,前面是"逗号不在反引号内".有人可以为我如何编写能够正确评估的代码提供一些指导吗?
作为一个例子,我目前需要一个宏,它采用一种描述形式的规则的形式,'(function-name column-index value)并生成一个谓词lambda体,以确定由column-index特定行索引的元素是否满足规则.如果我用规则调用这个宏'(< 1 2),我希望生成一个看起来如下的lambda体:
(lambda (row)
(< (svref row 1) 2))
Run Code Online (Sandbox Code Playgroud)
我能做的最好的努力如下:
(defmacro row-satisfies-rule (rule)
(let ((x (gensym)))
`(let ((,x ,rule))
(lambda (row)
(`,(car ,x) (svref row `,(cadr ,x)) `,(caddr ,x))))))
Run Code Online (Sandbox Code Playgroud)
经过评估,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}>
Run Code Online (Sandbox Code Playgroud)
如何编写宏来生成我需要的代码,特别是如何实现row-satisfies-rule?
使用来自Ivijay和纪律的想法,我修改了宏以便编译和工作,甚至允许将表单作为参数传递.它与我原先计划的宏运行方式略有不同,因为我确定将其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))))))
Run Code Online (Sandbox Code Playgroud)
此外,非常感谢使用干净,Lispy方法获取宏以生成代码以在运行时正确评估参数的解释.
sma*_*ane 12
首先,Lisp宏有"解构"参数列表.这是一个很好的功能,这意味着不是使用参数列表(rule)然后将其拆分(car rule) (cadr rule) (caddr rule),您可以简单地创建参数列表((function-name column-index value)).这样宏就会将三个元素的列表作为参数,然后将列表中的每个元素绑定到arguemnt列表中的相应符号.你可以使用或不使用,但它通常更方便.
接下来,`,实际上没有做任何事情,因为反引用告诉Lisp不要评估下面的表达式,并且逗号告诉它毕竟评估它.我认为你的意思是,(car x),只是评估(car x).如果使用解构参数,这不是问题.
既然你没有在宏扩展中引入任何新变量,我认为(gensym)在这种情况下是不必要的.
所以我们可以像这样重写宏:
(defmacro row-satisfies-rule ((function-name column-index value))
`(lambda (row)
(,function-name (svref row ,column-index) ,value)))
Run Code Online (Sandbox Code Playgroud)
这扩展了你想要的方式:
(macroexpand-1 '(row-satisfies-rule (< 1 2)))
=> (LAMBDA (ROW) (< (SVREF ROW 1) 2))
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助!
如果您需要评估参数以获取规则集,那么这是一个很好的方法:
(defmacro row-satisfies-rule (rule)
(destructuring-bind (function-name column-index value) (eval rule)
`(lambda (row)
(,function-name (svref row ,column-index) ,value))))
Run Code Online (Sandbox Code Playgroud)
这是一个例子:
(let ((rules '((< 1 2) (> 3 4))))
(macroexpand-1 '(row-satisfies-rule (car rules))))
=> (LAMBDA (ROW) (< (SVREF ROW 1) 2))
Run Code Online (Sandbox Code Playgroud)
就像以前那样.
如果你想要包含row在宏中,并让它直接给你答案,而不是做一个函数来做到这一点,试试这个:
(defmacro row-satisfies-rule-p (row rule)
(destructuring-bind (function-name column-index value) rule
`(,function-name (svref ,row ,column-index) ,value)))
Run Code Online (Sandbox Code Playgroud)
或者如果你需要评估rule参数(例如传递'(< 1 2)或(car rules)代替(< 1 2)),那么就使用(destructuring-bind (function-name column-index value) (eval rule)
实际上,对于你想要做的事情,函数似乎比宏更合适.只是
(defun row-satisfies-rule-p (row rule)
(destructuring-bind (function-name column-index value) rule
(funcall function-name (svref row column-index) value)))
Run Code Online (Sandbox Code Playgroud)
与宏相同的工作方式和更整洁,没有所有后悔的混乱担心.
一般来说,将宏用于可以通过函数完成的事情是不好的Lisp风格.
要理解的一点是,反引号功能与宏完全无关.它可以用于列表创建.由于源代码通常由列表组成,因此它可能在宏中很方便.
CL-USER 4 > `((+ 1 2) ,(+ 2 3))
((+ 1 2) 5)
Run Code Online (Sandbox Code Playgroud)
反引用引入了引用列表.逗号执行取消引用:评估逗号后的表达式并插入结果.逗号属于反引号:逗号仅在反引号表达式中有效.
另请注意,这是Lisp阅读器的严格特征.
以上基本类似于:
CL-USER 5 > (list '(+ 1 2) (+ 2 3))
((+ 1 2) 5)
Run Code Online (Sandbox Code Playgroud)
这将创建一个包含第一个表达式(未计算,因为引用)和第二个表达式结果的新列表.
为什么Lisp提供反引号表示法?
因为当想要创建大多数元素未被评估的列表时,它提供了一个简单的模板机制,但有一些是.此外,反引号列表看起来类似于结果列表.
| 归档时间: |
|
| 查看次数: |
4871 次 |
| 最近记录: |