【发布时间】:2017-02-17 16:50:33
【问题描述】:
我使用宏实现了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 列表?还是有另一种或多或少标准(并且可能更优雅)的解决方案?我强烈怀疑后者。
非常感谢您的意见。与往常一样,代码中的任何其他 cmets 也会受到赞赏。
更新
太棒了!我选择将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)))))
最后,我尝试在do 之后删除progn,但它不起作用,因为此时必须评估 2 个表达式。
【问题讨论】:
标签: macros lisp common-lisp