能否在Lisp宏lambda列表中使用不确定数量的解构列表?
实现类似let语法的多函数调用宏
需求概述
需要编写一个宏foo,其调用语法模仿Common Lisp的let特殊运算符:
(foo ((fn1 arg1) (fn2 arg2) ... (fnn argn)) body)
该宏展开后需生成包含多组funcall调用的代码,最终结构如下:
(progn (funcall fn1 arg1) (funcall fn2 arg2) ... (funcall fnn argn) body)
遇到的问题
宏lambda列表的解构特性无法直接实现“接受任意数量的(fn arg)结构”:
- 用
(&rest call-forms)可以接收所有调用形式,但无法直接在lambda列表层面解构每个(fn arg) - 定义固定的
((single-fn single-arg))只能处理单个调用形式,无法支持数量不确定的情况 - 尝试
((&rest (fn arg)) &body body)的写法无效,属于语法错误
解决方案
虽然宏lambda列表不支持直接解构&rest中的每个元素,但可以在宏体内部手动遍历并解构每个调用形式。
基础实现
(defmacro foo ((&rest call-forms) &body body) `(progn ,@(loop for (fn arg) in call-forms collect `(funcall ,fn ,arg)) ,@body))
代码解释
(&rest call-forms)接收所有传入的(fn arg)形式组成的列表- 在宏展开阶段,通过
loop遍历call-forms,将每个元素解构为fn和arg,生成对应的(funcall ,fn ,arg)表达式 - 使用
,@将生成的funcall表达式列表和body内容拼接进progn中,完成最终展开
带参数校验的增强实现
为避免传入不符合格式的调用形式,可以在宏展开阶段添加参数校验:
(defmacro foo ((&rest call-forms) &body body) ;; 校验每个调用形式是否为 (fn arg) 格式 (dolist (form call-forms) (unless (and (listp form) (= (length form) 2)) (error "无效的调用形式: ~a,必须是 (fn arg) 的二元列表" form))) `(progn ,@(loop for (fn arg) in call-forms collect `(funcall ,fn ,arg)) ,@body))
测试示例
调用代码:
(foo ((#'+ 1) (#'print "hello")) (format t "done~%"))
展开后生成:
(progn (funcall #'+ 1) (funcall #'print "hello") (format t "done~%"))
内容的提问来源于stack exchange,提问作者David Cleland
相关产品推荐
相关产品推荐

