gpt4 book ai didi

macros - Common Lisp 反引号/反引号 : How to Use?

转载 作者:太空宇宙 更新时间:2023-11-03 18:34:21 25 4
gpt4 key购买 nike

我在使用 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 方法的解释。

最佳答案

首先,Lisp 宏有“解构”参数列表。这是一个很好的特性,意味着不用参数列表 (rule)然后用 (car rule) (cadr rule) (caddr rule) 拆开它,您可以简单地制作参数列表 ((function-name column-index value)) .这样宏需要一个包含三个元素的列表作为参数,然后列表中的每个元素都绑定(bind)到 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)))

这正是您想要的扩展方式:

(macroexpand-1 '(row-satisfies-rule (< 1 2)))
=> (LAMBDA (ROW) (< (SVREF ROW 1) 2))

希望这对您有所帮助!


如果您需要评估参数以获得规则集,那么这里有一个很好的方法:

(defmacro row-satisfies-rule (rule)
(destructuring-bind (function-name column-index value) (eval rule)
`(lambda (row)
(,function-name (svref row ,column-index) ,value))))

这是一个例子:

(let ((rules '((< 1 2) (> 3 4))))
(macroexpand-1 '(row-satisfies-rule (car rules))))
=> (LAMBDA (ROW) (< (SVREF ROW 1) 2))

就像以前一样。


如果你想包括row在宏中让它直接给你答案而不是创建一个函数来做到这一点,试试这个:

(defmacro row-satisfies-rule-p (row rule)
(destructuring-bind (function-name column-index value) rule
`(,function-name (svref ,row ,column-index) ,value)))

或者如果您需要评估 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)))

与宏的工作方式相同,而且更简洁,无需担心反引号困惑。

一般来说,将宏用于函数可以完成的事情是不好的 Lisp 风格。

关于macros - Common Lisp 反引号/反引号 : How to Use?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6474777/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com