
[英]Predefining functions that are automatically generated with Common Lisp macros
[英]Invoking Common Lisp macros systematically with varying expressions
我正在学习 Common Lisp (SBCL)。
我想创建一个工具来调用两个(或更多)具有几个仅在某些参数上不同的相似表达式的宏。
我想定义表达式的基础,然后用我提供的参数修改它。 为此,我想到了 lambda function 的定义。
据我所知,没有与宏的funcall
类似的东西,所以我也将宏包装在 lambda 中。
我觉得我对所有这些lambda
-s 和funcall
-s 过于复杂。 有没有更优雅的方式?
宏来自外部库,所以我不想修改它们。
(特别是 fiveam 测试库的finishes
和signals
。)
这是一个示例代码:
(defmacro macro1 (body) ())
(defmacro macro2 (body) ())
(defun check-expr-with-args (do-m func args)
(dolist (arg args)
(format t "~a " arg)
(funcall do-m (lambda () (funcall func arg)))))
(let ((test-expr
#'(lambda (val) (format t "~a" val)))
(cases (list
(list #'(lambda (func) ( macro1 (funcall func)))
(list 1 2 3 4 5))
(list #'(lambda (func) ( macro2 (funcall func)))
(list -4 -5 -6 -7 -8 -9)))))
(dolist (c cases)
(check-expr-with-args (first c) test-expr (second c))))
最初我试图将宏名称传递给我的check-expr-with-args
function,以及引用形式的表达式,依赖于参数插入的词法范围。 那没有成功。
我认为您可以编写一个包装器宏来生成调用macro1
(和macro2
)的代码。 例如,我在这里定义m1
,它采用 (i) 测试表达式和 (ii) 预期在运行时计算值列表的表达式。
(defmacro m1 (test-expr input-expr)
(let ((arg (gensym)))
`(dolist (,arg ,input-expr)
(macro1 ,test-expr ,arg))))
test-expr
和input-expr
都被注入到一个dolist
表达式中,它绑定了一个名为arg
的变量。 这里arg
是与gensym
一起引入的新符号,以避免意外遮盖可能在test-expr
中使用的变量或符号宏。
例如:
(m1 (some-test-p) (list 1 2 3 4))
以上展开为:
(DOLIST (#:G1909 (LIST 1 2 3 4))
(MACRO1 (SOME-TEST-P) #:G1909))
结果表达式包含MACRO1
,它也将被扩展。 但它现在被包裹在一个表达式中,该表达式迭代运行时计算的一些列表。 在这里,它是一个常量,但您可以将其替换为任何其他表达式。
总之,通常最好通过将您自己的宏扩展到其他宏来在宏级别组合宏。
我想创建一个工具来调用两个(或更多)宏
(defmacro invoke-macros (macro-forms)
`(progn ,@macro-forms))
具有几个相似的表达式,仅在某些参数上有所不同。
(defmacro invoke-macros (macro-names &rest macro-arguments)
`(progn ,@(loop for m in macro-names
appending (loop for a in macro-arguments
collecting `(,m ,@a)))))
查看:
[1]> (macroexpand '(invoke-macros (m1 m2) (a b c) (d e f)))
(PROGN (M1 A B C) (M1 D E F) (M2 A B C) (M2 D E F)) ;
T
当然,这适用于任何运算符,包括函数,而不仅仅是宏; 我们应该称之为invoke-operators
。 或者一些更好的名字,反映我们正在从运算符和参数语法创建笛卡尔积。
如果我们需要返回值,我们可以将progn
更改为list
。 或者如果预计组合的数量不会很大,则可能是values
。
如果这必须是 function:
(defun invoke-ops (op-names &rest op-arguments)
(loop for o in op-names
appending (loop for a in op-arguments
collecting (eval `(,o ,@a)))))
查看:
[1]> (invoke-ops '(list +) '(1 2) '(10 20))
((1 2) (10 20) 3 30)
由于invoke-ops
现在是 function,我们必须引用 arguments。
宏没有funcall
。 如果您可以访问宏的扩展器 function,则有一个函数调用,但它所做的只是执行代码转换; 它不会调用宏。
调用由宏生成的代码的唯一方法是:您可以eval
代码,或者您可以compile
代码并funcall
它。 后一种方法需要 function,因此您首先将代码放入lambda
表达式中:
(funcall (compile nil `(lambda () ,code-output-by-macro)))
compile
的nil
参数是 function 名称; 我们告诉compile
器我们没有处理命名为 function 的定义。 我们在第二个参数中提供代码。 在某些 Common Lisp 实现中,没有求值器; eval
function 做了类似的事情:
(defun eval (code)
(funcall (compile nil `(lambda () ,code))))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.