2

鉴于下面这个可悲的事情,它会生成所有只有两个范围的对 -

[53]> (setq thingie '())

NIL
[54]> (loop for i in (generate-range 0 3) do 
(loop for j in (generate-range 4 6) do 
(push (list i j) thingie)))

NIL
[55]> thingie

((3 6) (3 5) (3 4) (2 6) (2 5) (2 4) (1 6) (1 5) (1 4) (0 6) (0 5) (0 4))
[56]>  

或者,换句话说,这会生成某种二维离散布局。

我将如何构建某种具有任意数量范围的对生成代码?(或生成 n 维离散布局)。

显然,一种解决方案是defmacro采用一个列表列表并构建n 个循环来执行,但这并不是一个简单的方法。

4

4 回答 4

2
(defun map-cartesian (fn bags)
  (labels ((gn (x y)
             (if y (mapc (lambda (i) (gn (cons i x) (cdr y))) (car y))
                 (funcall fn x))))
    (gn nil (reverse bags))))

CL-USER> (map-cartesian #'print '((1 2) (a b c) (x y)))

(1 A X) 
(2 A X) 
(1 B X) 
(2 B X) 
(1 C X) 
(2 C X) 
(1 A Y) 
(2 A Y) 
(1 B Y) 
(2 B Y) 
(1 C Y) 
(2 C Y) 

如果您更喜欢语法糖,

(defmacro do-cartesian ((item bags) &body body)
  `(map-cartesian (lambda (,item) ,@body) ,bags))

CL-USER> (do-cartesian (x '((1 2) (a b c) (x y)))
           (print x))

编辑:(简要说明)

gn 的第一个参数 x 是目前构建的部分元组;y 是剩余的元素包。函数 gn 通过迭代剩余袋子之一(car y)的每个元素 i 来扩展部分元组,以形成(cons ix)。当没有剩余的包时(if语句的 else 分支),元组完成,所以我们在元组上调用提供的函数 fn。

于 2010-09-09T10:22:16.880 回答
0

对我来说显而易见的是递归函数。

于 2010-09-09T01:07:18.947 回答
0

如果您认为这是一个控制结构,那么宏观路线就是要走的路。如果您将此视为一种生成数据的方式,那么递归函数就是您要走的路。

于 2010-09-09T09:43:09.123 回答
0

您不需要显式递归(甚至宏),这也可以通过高阶函数来完成:

(defun tuples-from-ranges (range &rest ranges)
  (reduce (lambda (acc range)
            (mapcan (lambda (sublist)
                      (mapcar (lambda (elt)
                                (append sublist (list elt)))
                              (apply #'generate-range range)))
                    acc))
          ranges
          :initial-value (mapcar #'list (apply #'generate-range range))))

两个嵌套的内部高阶函数 (mapcanmapcar) 执行与示例中的两个嵌套循环相同的功能。然后,外部高阶函数reduce将首先将前两个范围的值组合成对,然后在其参数函数的每次调用中再次将某个过程应用于来自先前调用和下一个范围的中间结果。

于 2013-09-08T02:37:58.960 回答