【发布时间】:2013-03-14 05:52:24
【问题描述】:
我正在尝试自学通用 lisp,并且作为宏编写练习,我正在尝试创建一个宏来定义任意深度的嵌套循环。我正在使用 sbcl,使用 emacs 和 slime。
首先,我编写了这个双循环宏:
(defmacro nested-do-2 (ii jj start end &body body)
`(do ((,ii ,start (1+ ,ii)))
((> ,ii ,end))
(do ((,jj ,ii (1+ ,jj)))
((> ,jj ,end))
,@body)))
然后我可以按如下方式使用:
(nested-do-2 ii jj 10 20 (print (+ ii jj)))
顺便说一句,我最初使用 gensym 编写此宏来生成循环计数器 (ii, jj),但后来我意识到如果我无法访问正文中的计数器,该宏将毫无用处。
无论如何,我想概括宏以创建一个嵌套执行循环,该循环将嵌套到任意级别。这是我到目前为止所得到的,但它并不完全有效:
(defmacro nested-do ((&rest indices) start end &body body)
`(dolist ((index ,indices))
(do ((index ,start (1+ index)))
((> index ,end))
(if (eql index (elt ,indices (elt (reverse ,indices) 0)))
,@body))))
我想调用如下:
(nested-do (ii jj kk) 10 15 (print (+ ii jj kk)))
但是,列表没有正确展开,我最终在调试器中出现以下错误:
error while parsing arguments to DEFMACRO DOLIST:
invalid number of elements in
((INDEX (II JJ KK)))
如果不是很明显,嵌入的 if 语句的重点是仅在最内层循环中执行主体。这对我来说似乎不是很优雅,也没有经过真正的测试(因为我还不能扩展参数列表),但这并不是这个问题的重点。
如何在宏中正确展开列表?是宏语法有问题,还是函数调用中列表的表达式有问题?任何其他 cmet 也将不胜感激。
提前致谢。
【问题讨论】:
-
为什么不把计数器列表当作一个参数呢?
(defmacro nested-do indices start end &body body),并且有(索引 ='(ii jj kk)) -
@ValekHalfHeart - 这也很好,但我认为代码与 (&rest 索引) 相比只是索引更能自我记录。当我做出改变时,我仍然无法让索引像我预期的那样扩展。
-
@huaiyuan - 感谢您的链接。那里有很多有趣的回应。您的回答简洁明了,我也喜欢 6502 的递归宏扩展。
标签: macros common-lisp