我使用宏实现了Heap's algorithm。它工作正常,但我想调整它,以便它可以根据需要生成照应或非照应代码。换句话说,我想让宏生成序列的内部副本,它将置换或处理宏外部可用的序列。
我完全不满意,彻头彻尾的令人尴尬的代码是:
;; Anaphoric version
;; To make it non-anaphoric, substitute (,var (copy-seq ,vec)) for (,var ,vec)
(defmacro run-permutations (var vec &rest body)
"Executes body for all permutations of vec, which is stored in variable var"
`(let ((,var ,vec))
(labels ((generate (&optional (n (length ,var)))
(if (= n 1)
(progn ,@body)
(progn
(loop for i from 0 below (1- n)
do (progn
(generate (1- n))
(rotatef (aref ,var (if (evenp n) i 0))
(aref ,var (1- n)))))
(generate (1- n))))))
(generate))))
? (run-permutations v "123" (pprint v))
"123"
"213"
"312"
"132"
"231"
"321"
?
我想写一些像这样的东西......
? (setf v "123")
? (run-permutations :anaphoric t v "123" (...do stuff...))
? v
"321"
? (setf v "123")
? (run-permutations v "123" (...do stuff...))
? v
"123"
...但我还没有找到令人满意的&rest
和&key
组合或任何其他编写lambda列表的方法。
所以我的问题是:有没有办法实现这一点,最好不要编写更多代码来解析宏的lambda列表?或者是否存在另一种或多或少标准(并且可能更优雅)的解决方案?我强烈怀疑后者。
非常感谢您的意见。与往常一样,对代码的任何其他评论也会受到赞赏。
更新
辉煌!我选择gensym
使用n
,因为body
是在递归中调用的,我无法看到如何从其他地方调用它 - 至少在没有重写所有内容的情况下
我还添加了另一项功能和次要优化。如果您有点好奇,更新后的版本是:
(defmacro do-permutations ((var vec &key anaphoric (len (length vec))) &body body)
"Executes body for all permutations of vec, which is stored in variable var.
KEYS:
anaphoric: if defined, modifies var outside the macro, preserves it otherwise
len: number of items that will be permuted, default is the full vector"
(let ((n (gensym)))
`(let ((,var ,(if anaphoric vec `(copy-seq ,vec))))
(labels ((generate (&optional (,n ,len))
(if (= ,n 1)
(progn ,@body)
(let ((n-1 (1- ,n)))
(loop for i from 0 below n-1
do (progn
(generate n-1)
(rotatef (aref ,var (if (evenp ,n) i 0))
(aref ,var n-1))))
(generate n-1)))))
(generate)))))
最后,我尝试在progn
之后移除do
,但它不起作用,因为此时必须对2个表达式进行评估。
答案 0 :(得分:4)
正确缩进代码:
(defmacro run-permutations (var vec &rest body)
"Executes body for all permutations of vec, which is stored in variable var"
`(let ((,var ,vec))
(labels ((generate (&optional (n (length ,var)))
(if (= n 1)
(progn ,@body)
(progn
(loop for i from 0 below (1- n)
do (progn
(generate (1- n))
(rotatef (aref ,var (if (evenp n) i 0))
(aref ,var (1- n)))))
(generate (1- n))))))
(generate))))
使用类似:
(do-permutations (v "123" :anaphoric t)
(some)
(stuff))
使用宏:
(defmacro do-permutations ((var vec &key anaphoric) &body body)
...)
其他名称:doing-permutations
,with-permutations
,...
另请注意,可以使用&body
声明正文,而不是&rest
。语义是相同的,但人们希望它的缩进方式不同。 &body
表示Lisp表单列表如下。
在progn
之后,您在loop
内也不需要do
。
body
看到变量n
。你可能会想到身体的另一个地方......