我正在尝试自学普通的lisp,作为宏写作练习,我正在尝试创建一个宏来定义任意深度的嵌套do循环。我正在使用sccl,使用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语句的要点是仅在最内层循环中执行主体。这对我来说似乎并不优雅,并且它没有经过测试(因为我还没有能够扩展参数列表),但这并不是这个问题的重点。
如何在宏中正确展开列表?是宏语法中的问题,还是函数调用中列表的表达式?任何其他意见也将不胜感激。
提前致谢。
答案 0 :(得分:1)
这是一种方法 - 从底部(循环体)构建每个索引的结构:
(defmacro nested-do ((&rest indices) start end &body body)
(let ((rez `(progn ,@body)))
(dolist (index (reverse indices) rez)
(setf rez
`(do ((,index ,start (1+ ,index)))
((> ,index ,end))
,rez)))))
答案 1 :(得分:0)
[除了投票之外,这实际上也有效,它也是美丽的!]
为了清楚地说明宏定义的递归性质,这里有一个Scheme实现:
(define-syntax nested-do
(syntax-rules ()
((_ ((index start end)) body)
(do ((index start (+ 1 index)))
((= index end))
body))
((_ ((index start end) rest ...) body)
(do ((index start (+ 1 index)))
((= index end))
(nested-do (rest ...) body)))))
使用上面的模板,像这样的东西可以完成它:
(defmacro nested-do ((&rest indices) start end &body body)
(let ((index (car indices)))
`(do ((,index ,start (1+ ,index)))
((> ,index ,end))
,(if (null (cdr indices))
`(progn ,@body)
`(nested-do (,@(cdr indices)) ,start ,end ,@body)))))
* (nested-do (i j) 0 2 (print (list i j)))
(0 0)
(0 1)
(0 2)
(1 0)
(1 1)
(1 2)
(2 0)
(2 1)
(2 2)
NIL
请注意,对于所有Common-Lisp宏,您需要使用' gensym'模式,以避免变量捕获。