我有一个函数替换列表中符号的所有实例:
(defun replace-symbol-in-sexp-fn (symbol-to-replace new-symbol sexp)
(if (eq sexp nil)
sexp
(cons
(if (listp (car sexp))
(replace-symbol-in-sexp-fn symbol-to-replace new-symbol (car sexp))
(if (eq (car sexp) symbol-to-replace)
(setf (car sexp) new-symbol)
(car sexp)))
(replace-symbol-in-sexp-fn symbol-to-replace new-symbol (cdr sexp)))))
(defmacro replace-symbol-in-sexp (symbol-to-replace new-symbol sexp)
`(replace-symbol-in-sexp-fn ,symbol-to-replace ,new-symbol ,sexp))
(macroexpand-1 (replace-symbol-in-sexp '+ '* (+ 2 3)))
; => TYPE-ERROR "The value 5 is not of type LIST" if sexp has comma,
; => UNBOUND-VARIABLE "The variable SEXP is unbound" if sexp has no comma
我在尝试评估最终表达式时遇到类型错误或未定义变量错误,具体取决于sexp是否在最后一行中以逗号开头。我给出了测试和替换 - symbol-in-sexp-fn的作品,比如说:
(replace-symbol-in-sexp-fn '+ '* '(+ 2 3)) ; => (* 2 3)
我现在尝试使用宏来生成这个,以便不必像'(+ 2 3)
那样引用sexp,因此我可以使用任意的lisp代码运行replace-symbol-in-sexp-fn 。显然,我可以评估并传入引用替换符号in-sexp-fn的性别,如:
(eval (replace-symbol-in-sexp-fn '+ '* '(+ 2 3))
但这是模仿宏的笨重尝试,所以我更愿意实际上只使用宏。是否有一种干净的方式来做我正在尝试用宏做的事情?我错过了什么?
答案 0 :(得分:1)
您似乎不想扩展到函数调用,而是使用该函数来扩展代码。你不应该引用它:
(defmacro replace-symbol-in-sexp (symbol-to-replace new-symbol sexp)
(replace-symbol-in-sexp-fn symbol-to-replace new-symbol sexp))
我的印象是你只是想重新实现像symbol-macrolet
这样的东西。
答案 1 :(得分:1)
所以你重新实现了Common Lisp函数nsubst。 subst
是正常版本,n
表示它是破坏性版本(不存在)。
请注意,在便携式Common Lisp中,修改文字数据并不是一个好主意。效果未定义。暂时忽略它:
(macroexpand-1 (replace-symbol-in-sexp '+ '* (+ 2 3)))
但可能你想宏扩展表达式而不是结果?可能应该是:
(macroexpand-1 '(replace-symbol-in-sexp '+ '* (+ 2 3)))
但那个宏毫无意义。生成的代码为false,因为最后一个参数未计算到列表。宏必须创建有用的代码。如你所见,最后一个表达式没有引用,这没有任何意义。
CL-USER 14 > (macroexpand-1 '(replace-symbol-in-sexp '+ '* (+ 2 3)))
(REPLACE-SYMBOL-IN-SEXP-FN (QUOTE +) (QUOTE *) (+ 2 3))
让我们来介绍一下:
(defmacro replace-symbol-in-sexp (symbol-to-replace new-symbol sexp)
`(replace-symbol-in-sexp-fn ,symbol-to-replace ,new-symbol ',sexp))
CL-USER 17 > (macroexpand-1 '(replace-symbol-in-sexp '+ '* (+ 2 3)))
(REPLACE-SYMBOL-IN-SEXP-FN (QUOTE +) (QUOTE *) (QUOTE (+ 2 3)))
这个宏有用吗?我有疑虑。