Lisp宏(或函数)用于嵌套循环

mmj*_*mmj 7 lisp macros recursion nested-loops

是否有可能编写一个Common Lisp宏来获取维度和变量列表,一个体(迭代),并创建由列表指定的嵌套循环组成的代码?

就是这样的:

(nested-loops '(2 5 3) '(i j k) whatever_loop_body)
Run Code Online (Sandbox Code Playgroud)

应扩大到

(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)))
Run Code Online (Sandbox Code Playgroud)

跟进

正如怀远正确指出的那样,我必须知道在编译时传递给宏的参数.如果你真的需要我的功能,请看下面.

如果你对宏有好处,那就选择6502的递归解决方案,真是太棒了.

hua*_*uan 7

您不需要引号,因为无论如何都需要在编译时知道维度和变量.

(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)))
Run Code Online (Sandbox Code Playgroud)

编辑:

如果在编译时无法确定维度,我们需要一个函数

(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)))
Run Code Online (Sandbox Code Playgroud)

并在呼叫时将身体包裹在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) 
...
Run Code Online (Sandbox Code Playgroud)

编辑(2012-04-16):

编写上面版本的嵌套映射以更接近地反映原始问题陈述.正如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))))
Run Code Online (Sandbox Code Playgroud)

然后,

CL-USER> (nested-map #'print '(2 3 4))
Run Code Online (Sandbox Code Playgroud)


650*_*502 7

有时一种有用的方法是编写递归宏,即生成包含同一宏的另一个调用的代码的宏,除非案例足够简单直接解决:

(defmacro nested-loops (max-values vars &rest body)
  (if vars
      `(loop for ,(first vars) from 0 to ,(first max-values) do
          (nested-loops ,(rest max-values) ,(rest vars) ,@body))
      `(progn ,@body)))

(nested-loops (2 3 4) (i j k)
  (print (list i j k)))
Run Code Online (Sandbox Code Playgroud)

在上面,如果变量列表为空,则宏直接扩展到正文表单,否则生成的代码是(loop...)第一个变量,包含(nested-loops ...)do部分中的另一个调用.

宏在用于函数的正常意义上不是递归的(它不直接调用自身),但是宏扩展逻辑将为内部部分调用相同的宏,直到代码生成完成为止.

请注意,内循环中使用的最大值表单将在外循环的每次迭代中重新计算.如果表单确实是测试用例中的数字,那么它没有任何区别,但如果它们是例如函数调用则不同.