2017-02-17 14 views
0

Я применил 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 выражения должны быть оценены в этой точке.

ответ

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 сигнализирует, что следует список форм Лиспа.

Вы также не нуждаетесь в progn в loop после do.

body видит переменную n. Вы можете подумать о другом месте для тела ...

+0

Знаете ли вы, что ссылка на правильный отступ в lisp? Я использовал форматирование по умолчанию для emacs и был бы заинтересован в альтернативе. –

+1

@JonChesterfield: см. Здесь: http://emacs.stackexchange.com/questions/30788/why-does-emacs-indent-my-lisp-loop-construct-weirdly –

+0

Спасибо! slime-cl-indent выглядит хорошо. –