在common-lisp中有一种方法可以为宏编写“apply”等价物吗?

时间:2017-11-25 09:54:18

标签: macros common-lisp

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

由于我不能将apply与宏一起使用,并且由于我无法控制宏的实现,如何创建给定列表的函数fun1 (lst),使用扩展列表调用宏?

更新:我虽然问题已经足够,但似乎更好地陈述我的真实案例:

后现代提供了查询宏:

macro: query (query &rest args/format) 

例如,您可以这样称呼它:

(query "select * from example where col1 = $1::integer and 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)) 

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

2 个答案:

答案 0 :(得分:3)

关于您的实际问题,请注意宏使用低级别包 cl-postgres 来使用函数实现查询(请参阅http://quickdocs.org/postmodern/api#system-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一样,您首先定义了准备好的语句,以避免注入攻击。如果您经常重复使用相同的查询,那么仅将它们准备一次会更有效(请参阅defprepared)。然后,执行查询。

当您还指定结果类型(查询中的最后一个关键字参数)时,宏会调用未导出的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注入,但EVAL打开另一个攻击向量;检查你的输入。)

或者,尝试了解如何使用查询的构建块来生成自己的查询解释器。乍一看,它似乎是某种内在的平台效应"获得任意查询并处理它们,但也许你有一个很好的用例。

答案 1 :(得分:2)

也可以使用COMPILE甚至COMPILE-FILE + LOAD来处理它。

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

原始版本:

  1. 生成一个文件,将IN-PACKAGE表单放入其中并将一个或多个查询表单转储到其中。
  2. 使用COMPILE-FILE
  3. 编译文件
  4. 处理错误......
  5. 加载文件然后执行查询
  6. 如果你想对结果做些什么,你必须在文件中加入更多的代码......

    此示例要求函数compile-file和必要的库/宏代码在运行时可用。