Macros 如何避免defmacro中的eval?
我编写了一个宏,它接受要调用的lambda列表并生成一个函数。lambda始终在Macros 如何避免defmacro中的eval?,macros,common-lisp,Macros,Common Lisp,我编写了一个宏,它接受要调用的lambda列表并生成一个函数。lambda始终在defun参数列表中计算,但不在defmacro中计算。如何避免在defmacro内部调用eval 此代码适用于: (defmacro defactor (name &rest fns) (let ((actors (gensym))) `(let (;(,actors ',fns) (,actors (loop for actor in ',fns
defun
参数列表中计算,但不在defmacro
中计算。如何避免在defmacro
内部调用eval
此代码适用于:
(defmacro defactor (name &rest fns)
(let ((actors (gensym)))
`(let (;(,actors ',fns)
(,actors (loop for actor in ',fns
collect (eval actor)))) ; This eval I want to avoid
(mapcar #'(lambda (x) (format t "Actor (type ~a): [~a]~&" (type-of x) x)) ,actors)
(defun ,name (in out &optional (pos 0))
(assert (stringp in))
(assert (streamp out))
(assert (or (plusp pos) (zerop pos)))
(loop for actor in ,actors
when (funcall actor in out pos)
return it)))))
;; Not-so-relevant use of defactor macros
(defactor invert-case
#'(lambda (str out pos)
(let ((ch (char str pos)))
(when (upper-case-p ch)
(format out "~a" (char-downcase ch))
(1+ pos))))
#'(lambda (str out pos)
(let ((ch (char str pos)))
(when (lower-case-p ch)
(format out "~a" (char-upcase ch))
(1+ pos)))))
此代码的计算结果与预期一致:
Actor (type FUNCTION): [#<FUNCTION (LAMBDA (STR OUT POS)) {100400221B}>]
Actor (type FUNCTION): [#<FUNCTION (LAMBDA (STR OUT POS)) {100400246B}>]
INVERT-CASE
其余的显然都不起作用
如果我将defmacro
转换为defun
,则不需要eval
:
(defun defactor (name &rest fns)
(defun name (in out &optional (pos 0))
(assert (stringp in))
(assert (streamp out))
(assert (or (plusp pos) (zerop pos)))
(loop for actor in fns
when (funcall actor in out pos)
return it)))
但是,它总是定义函数名,而不是传递的函数名参数(应该引用)
是否可以编写
defactor
,并且可以传递函数名,而不是defun
版本,并且在宏版本中不使用eval
?第一个循环会使事情变得更加复杂。。。只需收集参数即可
(defmacro defactor (name &rest fns)
(let ((actors (gensym)))
`(let ((,actors (list ,@fns)))
(mapcar #'(lambda (x) (format t "Actor (type ~a): [~a]~&" (type-of x) x)) ,actors)
(defun ,name (in out &optional (pos 0))
(assert (stringp in))
(assert (streamp out))
(assert (or (plusp pos) (zerop pos)))
(loop for actor in ,actors
when (funcall actor in out pos)
return it)))))
这基本上不需要是一个宏。您主要可以使用辅助功能:
(defun make-actor (&rest funs)
(lambda (in out &optional (pos 0)
(loop for actor in funs
when (funcall actor in out pos) return it)))
并编写一个简单的宏:
(defmacro defactor (name &rest funs)
`(let ((f (make-actor ,@funs)))
(defun ,name (in out &optional (pos 0)) (funcall f in out pos))))
然而,这在表达能力(你实际上像调用函数一样调用宏)或效率(编译器必须非常聪明地解决如何通过倾斜一堆复杂的东西来改进代码)方面并没有什么好处
下面是另一种实现类似的方法:
(defmacro defactor (name (in out pos) &rest actors)
(let ((inv (gensym "IN"))
(outv (gensym "OUT"))
(posv (gensym "POS")))
`(defun ,name (,inv ,outv &optional (,posv 0))
;; TODO: (declare (type ...) ...)
(or ,@(loop for form in actors
collect `(let ((,in ,inv) (,out ,outv) (,pos ,posv)) ,form)))))
然后像这样使用它:
(defactor invert-case (in out pos)
(let ((ch (char str pos)))
(when (upper-case-p ch)
(format out "~a" (char-downcase ch))
(1+ pos)))
(let ((ch (char str pos)))
(when (lower-case-p ch)
(format out "~a" (char-upcase ch))
(1+ pos))))
您可以用(list,@fns)
@jkiiski替换循环,不幸的是,这不起作用:;LET绑定规范(MAPCAR;#’(LAMBDA(X);(格式T“Actor(type~a):[~a]~&”(type-OF X)X));#:G647)的格式不正确。
这听起来像是LET绑定缺少一个右括号,所以它也将下面的MAPCAR
表单作为绑定;LET绑定规范(反吹倒壳(输入输出和可选(位置0))…的格式不正确。
它的作用是什么?对于这个简单的例子来说,看起来非常复杂。Dan Robertson,你的观察的确很有趣。现在我将用第一个解决方案来说明问题,因为过程线
将与由不同宏创建的参与者一起工作,而不仅仅是defactor
(我在deftransformer
中重命名了它)。
(defmacro defactor (name (in out pos) &rest actors)
(let ((inv (gensym "IN"))
(outv (gensym "OUT"))
(posv (gensym "POS")))
`(defun ,name (,inv ,outv &optional (,posv 0))
;; TODO: (declare (type ...) ...)
(or ,@(loop for form in actors
collect `(let ((,in ,inv) (,out ,outv) (,pos ,posv)) ,form)))))
(defactor invert-case (in out pos)
(let ((ch (char str pos)))
(when (upper-case-p ch)
(format out "~a" (char-downcase ch))
(1+ pos)))
(let ((ch (char str pos)))
(when (lower-case-p ch)
(format out "~a" (char-upcase ch))
(1+ pos))))