生成对组合的更通用的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只是一个通用的名字,没有任何意义;我太懒了,想不出一个有意义的标签。有关简要说明,请参见编辑。