生成对组合的更通用的lisp代码

生成对组合的更通用的lisp代码,lisp,common-lisp,cartesian,Lisp,Common Lisp,Cartesian,考虑到下面这个令人伤心的事情,它只生成两个范围的所有对- [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)

考虑到下面这个令人伤心的事情,它只生成两个范围的所有对-

[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个循环来执行,但这并不是一个简单的方法。

对我来说,最明显的是一个递归函数。

如果你认为这是一个控制结构,那么宏路由就是一种方法。如果您认为这是一种生成数据的方法,那么递归函数就是一种方法

(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 i x)。当没有剩余的行李(if语句的else分支)时,元组完成,因此我们调用元组上提供的函数fn。

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

(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))))

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

好的,我已经盯着你的代码看了一会儿,但我不明白,即使在重新格式化和重命名变量之后。你能解释一下gn吗(gn是什么意思?)对不起,gn只是一个通用的名字,没有任何意义;我太懒了,想不出一个有意义的标签。有关简要说明,请参见编辑。