Более аккуратный способ подсунуть ЗАЯВЛЕНИЕ от & тела - PullRequest
3 голосов
/ 23 марта 2012

Я пишу макрос, который генерирует вызов DEFUN - соответственно, я хочу убедиться, что любые DECLARE в теле макроса размещаются сразу после DEFUN.Вот что у меня есть:

(defmacro defsynced (name (&rest args) &body body)
  (let* ((decl (if (eql (caar body) 'cl:declare)
                   (list (car body))))
         (body (if decl
                   (cdr body)
                   body)))
    `(defun ,name ,args
       ,@decl
       (bordeaux-threads:with-lock-held (*request-lock*)
         ,@body))))

К сожалению, это довольно некрасиво и не обязательно очевидно, что здесь происходит.Вы можете придумать более приятный способ?

Ответы [ 3 ]

3 голосов
/ 23 марта 2012

Ваше решение не является полным, потому что может быть одно или более объявлений.

Хотя вы можете использовать некоторые готовые функции для этого, оно хорошо изучает техникуэто может быть полезно в подобных ситуациях.

Если у вас есть список в форме

(alpha beta x epsilon ... omega)

, где x - это интересующий вас объект, на который вы хотите разбить список, вы можете использоватьфункция member для поиска подсписка, который начинается с x, а затем функция ldiff для извлечения префикса из этого списка (alpha beta), исключая (x epsilon omega).Первый шаг:

(member-if-not (lambda (x) (eq x 'declare)) '(declare declare 3 4 5))

-> (3 4 5)

Конечно, мы ищем (declare ...), а не declare.Мы не можем использовать :key #'car для этого, потому что формы не могут быть conses, поэтому:

(member-if-not (lambda (x) (and (consp x) (eq (car x) 'declare)))
               '((declare foo) (declare bar) 3 4 5))
-> (3 4 5)

Теперь, как получить объявления и остальные формы самостоятельно:

(defun separate-decls-and-body (body)
  (let* ((just-the-code (member-if-not (lambda (x)
                                         (and (consp x) (eq (car x) 'declare)))
                                        body))
         (just-the-decls (ldiff body just-the-code)))
    (values just-the-decls just-the-code)))

Тесты:

> (separate-decls-and-body '((declare (optimize (speed 3))) (declare (type)) 1 2 3))
((DECLARE (OPTIMIZE (SPEED 3))) (DECLARE (TYPE))) ;
(1 2 3)

> (separate-decls-and-body '((declare (optimize (speed 3)))))
((DECLARE (OPTIMIZE (SPEED 3)))) ;
NIL

> (separate-decls-and-body '())
NIL ;
NIL

> (separate-decls-and-body '(1 2 3))
NIL ;
(1 2 3)

Семья member и ldiff - ваши друзья.ldiff основан на том факте, что member возвращает подструктуру исходного списка, а не копию;он просто перемещается по списку в поисках этого указателя и возвращает все предыдущие элементы в виде нового списка.

1 голос
/ 23 марта 2012

Ни один ответ не полон без ритуальной жертвы божеству loop:

(defun separate-decls-and-body (body)
  (loop for sub-body on body
        for form = (first sub-body) 
        until (or (atom form) (not (eq (first form) 'declare)))
        collecting form into decls
        finally (return (values decls sub-body))))
1 голос
/ 23 марта 2012

Поскольку анализ объявлений может быть сложным, существует библиотека с именем parse-Declarations , которая помогает с этим. Доступна с Quicklisp .

(ql:quickload "parse-declarations-1.0")

Функция parse-body особенно актуальна для вашего вопроса.

...