Macros 在common lisp中,是否有一种方法可以为宏编写“应用”等效代码?

Macros 在common lisp中,是否有一种方法可以为宏编写“应用”等效代码?,macros,common-lisp,Macros,Common Lisp,我有一个宏:mac1&rest args,它接受任意数量的参数 既然我不能对宏使用apply,而且我对宏的实现没有控制权,那么如何使给定列表的函数fun1 lst使用扩展列表调用宏呢 更新:我认为这个问题已经足够了,但最好陈述一下我的真实情况: 后现代提供了查询宏: macro: query (query &rest args/format) 例如,您这样称呼它: 质疑 从col1=$1::integer和col2=$2::date的示例中选择* 123 2017-01-01 :st

我有一个宏:mac1&rest args,它接受任意数量的参数

既然我不能对宏使用apply,而且我对宏的实现没有控制权,那么如何使给定列表的函数fun1 lst使用扩展列表调用宏呢

更新:我认为这个问题已经足够了,但最好陈述一下我的真实情况:

后现代提供了查询宏:

macro: query (query &rest args/format) 
例如,您这样称呼它:

质疑 从col1=$1::integer和col2=$2::date的示例中选择* 123 2017-01-01 :str alists

好的,现在想象一下,在运行时,我通过了以下列表:

'((:query "select * from table1 where c1 = $1 and c2 = $2 and c3 = $3"
   :params (1 "2017-01-01" 3) :return :str-alists

  (:query "select * from tab2 where c3 = $1"
   :params ("somevalue") :return :lists)
   .
   .
   .
  ))
我必须做查询。我需要定义一个函数,它将把这样一个列表作为参数并执行查询。但由于查询是一个宏,我有以下问题:我需要一个地方来计算列表,以便将它们传递给宏。目前,我的想法来自@melpomene评论:

(defun macro-apply(q p ret)
  (eval (macroexpand `(query ,q ,@p ,ret))))
因此,函数变成:

(defun exec-queries (lst)
    (mapc 
       (lambda(x) (macro-apply 
                      (getf x :query) 
                      (getf x :params) 
                      (getf x :return))
       lst)) 

有没有更好的方法,或者我缺少的一些明显的方法?

也可以使用COMPILE甚至COMPILE-FILE+LOAD来处理这个问题

例如,在某些情况下,以某种格式输出查询表单并将其嵌入到文件中可能很有用。然后,您可以编译该文件并加载代码,以便在加载时执行或稍后执行

原始版本:

生成一个文件,将包内表单放入其中,并将一个或多个查询表单转储到其中。 使用compile-file编译文件 处理错误。。。 加载文件然后执行查询 如果你想对结果做点什么,你必须在文件中加入更多的代码


此示例要求函数编译文件和必要的库/宏代码在运行时可用。

关于实际问题,请注意,宏使用低级包cl postgres来使用函数实现查询,请参阅。宏只是底层API的语法糖。让我们看看宏是如何通过参数化查询展开的:

CL-USER> (macroexpand
           '(postmodern:query 
               "select * from table where col1=$1:integer" 
               20))

(PROGN
 (CL-POSTGRES:PREPARE-QUERY POSTMODERN:*DATABASE* ""
                            "select * from table where col1=$1:integer")
 (CL-POSTGRES:EXEC-PREPARED POSTMODERN:*DATABASE* "" (LIST 20)
                            'CL-POSTGRES:LIST-ROW-READER))
与SQL一样,首先定义准备好的语句,以避免注入攻击。如果经常重用相同的查询,只准备一次查询会更有效。然后,执行查询

当您还从查询中指定result type last关键字参数时,宏将调用unexported reader for format函数,以了解对每行使用哪个回调函数

(macroexpand
 '(postmodern:query "select * from table where col1=$1:integer"
   20
   :str-alist))
(MULTIPLE-VALUE-CALL
    #'(LAMBDA
          (&OPTIONAL (POSTMODERN::ROWS) (POSTMODERN::AFFECTED) &REST #:G843)
        (DECLARE (IGNORE #:G843))
        (IF POSTMODERN::AFFECTED
            (VALUES (CAR POSTMODERN::ROWS) POSTMODERN::AFFECTED)
            (CAR POSTMODERN::ROWS)))
  (PROGN
   (CL-POSTGRES:PREPARE-QUERY POSTMODERN:*DATABASE* ""
                              "select * from table where col1=$1:integer")
   (CL-POSTGRES:EXEC-PREPARED POSTMODERN:*DATABASE* "" (LIST 20)
                              'CL-POSTGRES:ALIST-ROW-READER)))
T
<>因为宏已经为你做了所有的工作,你可以有效地包装你的查询并评估它们,如果你不评估任意的LISP格式的准备语句保护你不受SQL注入,但是EVE打开另一个攻击向量;检查您的输入


或者,尝试了解如何使用查询的构建块来生成您自己的查询解释器。乍一看,获取任意查询并对其进行处理似乎是某种内部平台效应,但也许您对此有一个很好的用例。

macroexpand`mac1,@lst?因为它是来自外部库的宏,而库不提供函数。通常,在函数中调用宏并直接传递其参数。但在我的例子中,在运行时,我会反复收到一些我事先不知道的参数列表,这些参数的长度不同,我被要求用这些参数调用宏。@melpomene我更新了我的问题以更好地说明。顺便说一句,你不需要调用MACROEXPAND。EVAL就是这么做的,一般来说就是这样。请注意,EVAL可能不会编译东西并使用解释器来运行它。此外,还需要解析重复的查询/。。。每次都会用到它们。听起来是这样,但事实并非如此。我仍将编写查询。我只是不想专门为人们可能想到的每个报表创建api端点和前端输入字段。@Paralife这是为编写SQL查询的专家用户设计的吗?只是好奇,这是给我的。我将写下这些问题。我收到了很多在web应用程序中提供自定义查询的请求,为了发布查询输入字段的组件或页面,我不得不在前端处理大量样板文件,api端点等。现在,我将为通用报告创建前端模板页/组件,并将查询和参数类型作为plist写入lisp文件中。我会将参数发送到客户端,它会呈现输入字段,返回参数值,我会将它们连同查询文本一起输入到我的函数中。我知道我可以编写最少的代码来直接调用每个报表的查询宏,但这是另一个麻烦,报表有时是ephimeral。现在我将有一个目录,其中的每个文件都包含plist中的报表定义。如果删除该文件,则报告将被删除。如果我添加一个文件,一份报告在美国神奇地可用
ers前端,不做任何其他事情。@Paralife感谢您的解释