我正在编写一个生成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))))
不幸的是,它相当难看,并不一定明显在这里发生了什么。有没有更好的方法可以想到?
答案 0 :(得分:3)
您的解决方案尚未完成,因为可以有一个或更多声明。
虽然你可以使用一些现成的功能,但它可以很好地研究一种在类似情况下有用的技术。
如果您有表格列表
(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
,因为表单可能不合适,所以:
(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 :(得分:1)
由于声明解析可能很棘手,因此有一个名为parse-declarations的库可以帮助解决它。它可以从Quicklisp获得。
(ql:quickload "parse-declarations-1.0")
parse-body
功能与您的问题特别相关。
答案 2 :(得分:1)
没有对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))))