Я применил Heap's algorithm с помощью макроса. Он работает нормально, но я хотел бы настроить его, чтобы он генерировал анафорический или нефакторный код по требованию. Другими словами, я хотел бы, чтобы макрос сделал внутреннюю копию последовательности, которую он перестановит или будет работать над последовательностью, доступной вне макроса.Каков наилучший способ комбинирования & ключа и & отдохнуть в lambda список lisp макроса?
Мой совершенно неудовлетворительно, совершенно неловко код:
;; 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
или любой другой подход для записи лямбда-списка.
Итак, мой вопрос: есть ли способ выполнить это, желательно без написания большего количества кода для анализа лямбда-списка макроса? Или есть другое, более или менее стандартное (и предположительно более элегантное) решение? Я сильно подозреваю последнего.
Ваш вклад очень ценится. Как всегда, любые другие комментарии к коду также ценятся.
UPDATE
Brilliant! Я решил использовать 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 выражения должны быть оценены в этой точке.
Знаете ли вы, что ссылка на правильный отступ в lisp? Я использовал форматирование по умолчанию для emacs и был бы заинтересован в альтернативе. –
@JonChesterfield: см. Здесь: http://emacs.stackexchange.com/questions/30788/why-does-emacs-indent-my-lisp-loop-construct-weirdly –
Спасибо! slime-cl-indent выглядит хорошо. –