You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在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))

代码解释

  1. (&rest call-forms)接收所有传入的(fn arg)形式组成的列表
  2. 在宏展开阶段,通过loop遍历call-forms,将每个元素解构为fn和arg,生成对应的(funcall ,fn ,arg)表达式
  3. 使用,@将生成的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 21:13:17