【问题标题】:how to pass quoted sexp to macro如何将引用的sexp传递给宏
【发布时间】:2014-11-23 23:58:27
【问题描述】:

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

(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。显然,我可以 eval 并传入引用的 sexp 以替换-symbol-in-sexp-fn,例如:

(eval (replace-symbol-in-sexp-fn '+ '* '(+ 2 3))

但这是一种笨拙的模仿宏的尝试,所以我宁愿实际上只使用宏。有没有一种干净的方法来做我想要用宏做的事情?我错过了什么?

【问题讨论】:

  • “我收到类型错误或未定义变量错误”我在问题中看不到任何一个。你能举个例子吗?
  • 经过精确编辑。

标签: macros lisp common-lisp quote


【解决方案1】:

所以你重新实现了 Common Lisp 函数nsubstsubst是普通版,n是破坏版(非consing)。

请注意,在可移植的 Common Lisp 中,修改文字数据不是一个好主意。效果未定义。暂时忽略:

(macroexpand-1 (replace-symbol-in-sexp '+ '* (+ 2 3)))

但您可能想对表达式而不是结果进行宏扩展?大概应该是:

(macroexpand-1 '(replace-symbol-in-sexp '+ '* (+ 2 3)))

但是那个宏没有意义。生成的代码是错误的,因为最后一个参数不会计算为列表。宏必须创建有用的代码。如您所见,最后一个表达式没有被引用,这没有任何意义。

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

那个宏有用吗?我有疑问。

【讨论】:

  • 这很有帮助;我没有意识到我可以在这样的逗号前加上引号。您对它的实用性有什么疑问?
  • @cosmicexplorer 我不能肯定地说,但我希望 Rainer 的观点是 replace-symbol-in-sexp 可能不是那么有用,因为它正如 Rainer 指出的那样,本质上是 substnsubst
  • @JoshuaTaylor 什么是只为第三个参数添加引号的宏?为什么不直接使用函数呢?
  • @RainerJoswig Oy,这是一个更好的观点。我刚刚抓住了你提到的第一件事。是的,一个很容易成为函数的宏也不是很有用(尤其是当函数已经存在时)。
  • 宏将 subst 转换为编译时间,这对我来说似乎更有意义。
【解决方案2】:

看来您并不想扩展为函数调用,而是使用函数来扩展您的代码。你不应该引用它:

(defmacro replace-symbol-in-sexp (symbol-to-replace new-symbol sexp)
  (replace-symbol-in-sexp-fn symbol-to-replace new-symbol sexp))

我的印象是您只是想重新实现symbol-macrolet 之类的东西。

【讨论】:

  • 嗯,更重要的是我不知道已经有此类功能的功能,因为当我搜索“在 sexp 中替换符号”时,我找不到对这些东西的引用在 CLHS 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-03
  • 1970-01-01
相关资源
最近更新 更多