如何将引用的sexp传递给宏

时间:2014-11-23 23:58:28

标签: macros lisp common-lisp quote

我有一个函数替换列表中符号的所有实例:

(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))

但这是模仿宏的笨重尝试,所以我更愿意实际上只使用宏。是否有一种干净的方式来做我正在尝试用宏做的事情?我错过了什么?

2 个答案:

答案 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函数nsubstsubst是正常版本,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)))

这个宏有用吗?我有疑虑。