Ваше решение не является полным, потому что может быть одно или более объявлений.
Хотя вы можете использовать некоторые готовые функции для этого, оно хорошо изучает техникуэто может быть полезно в подобных ситуациях.
Если у вас есть список в форме
(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
возвращает подструктуру исходного списка, а не копию;он просто перемещается по списку в поисках этого указателя и возвращает все предыдущие элементы в виде нового списка.