是否可以编写一个 Common Lisp 宏,它采用维度和变量列表、(迭代的)主体,并创建由列表指定的嵌套循环组成的代码?
也就是说,类似于:
(nested-loops '(2 5 3) '(i j k) whatever_loop_body)
应该扩展到
(loop for i from 0 below 2 do
(loop for j from 0 below 5 do
(loop for k from 0 below 3 do
whatever_loop_body)))
跟进
正如怀远正确指出的那样,我必须知道在编译时传递给宏的参数。如果你真的像我一样需要一个功能,请看下面。
如果您对宏没问题,请使用 6502 的递归解决方案,非常棒。
最佳答案
您不需要引号,因为维度和变量无论如何都需要在编译时知道。
(defmacro nested-loops (dimensions variables &body body)
(loop for range in (reverse dimensions)
for index in (reverse variables)
for x = body then (list y)
for y = `(loop for ,index from 0 to ,range do ,@x)
finally (return y)))
编辑:
如果维度不能在编译时决定,我们就需要一个函数
(defun nested-map (fn dimensions)
(labels ((gn (args dimensions)
(if dimensions
(loop for i from 0 to (car dimensions) do
(gn (cons i args) (cdr dimensions)))
(apply fn (reverse args)))))
(gn nil dimensions)))
并在调用时将主体包裹在 lambda 中。
CL-USER> (nested-map (lambda (&rest indexes) (print indexes)) '(2 3 4))
(0 0 0)
(0 0 1)
(0 0 2)
(0 0 3)
(0 0 4)
(0 1 0)
(0 1 1)
(0 1 2)
(0 1 3)
(0 1 4)
(0 2 0)
(0 2 1)
...
编辑(2012-04-16):
以上版本的 nested-map 是为了更准确地反射(reflect)原始问题陈述而编写的。正如 mmj 在评论中所说,使索引范围从 0 到 n-1 可能更自然,如果我们不坚持以行为主的迭代顺序,则将反转移出内部循环应该会提高效率。此外,让输入函数接受一个元组而不是单个索引可能更明智,以便与排名无关。这是一个具有所述更改的新版本:
(defun nested-map (fn dimensions)
(labels ((gn (args dimensions)
(if dimensions
(loop for i below (car dimensions) do
(gn (cons i args) (cdr dimensions)))
(funcall fn args))))
(gn nil (reverse dimensions))))
然后,
CL-USER> (nested-map #'print '(2 3 4))
关于recursion - 用于嵌套循环的 Lisp 宏(或函数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10163298/