gpt4 book ai didi

macros - 如何避免在 defmacro 中进行评估?

转载 作者:行者123 更新时间:2023-12-01 16:13:49 24 4
gpt4 key购买 nike

我编写了一个宏,它接受要调用的 lambda 列表并生成一个函数。 lambda 始终在 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

它的用法是:

;; Complete example
(defun process-line (str &rest actors)
(assert (stringp str))
(with-output-to-string (out)
(loop for pos = 0 then (if success success (1+ pos))
for len = (length str)
for success = (loop for actor in actors
for ln = len
for result = (if (< pos len)
(funcall actor str out pos)
nil)
when result return it)
while (< pos len)
unless success do (format out "~a" (char str pos)))))

(process-line "InVeRt CaSe" #'invert-case) ; evaluates to "iNvErT cAsE" as expected

没有 eval,上面的 defactor 计算为:

Actor (type CONS): [#'(LAMBDA (STR OUT POS)
(LET ((CH (CHAR STR POS)))
(WHEN (UPPER-CASE-P CH)
(FORMAT OUT ~a (CHAR-DOWNCASE CH))
(1+ POS))))]
Actor (type CONS): [#'(LAMBDA (STR OUT POS)
(LET ((CH (CHAR STR POS)))
(WHEN (LOWER-CASE-P CH)
(FORMAT OUT ~a (CHAR-UPCASE CH))
(1+ POS))))]

其余的显然都行不通。

如果我将 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)))

但是,它总是定义函数 name 而不是传递的函数名称参数(应该用引号引起来)。

是否可以编写 defactor 并传递不同于 defun 版本的函数名,并且在 macro 中不使用 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)))))

关于macros - 如何避免在 defmacro 中进行评估?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55651209/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com