我不知道lisp宏如何构建它的扩张?确切的步骤是什么?

时间:2012-08-28 02:02:21

标签: emacs macros lisp expansion

我尝试编写宏并按如下方式执行。但它未能执行。

(defmacro times_two (var) (* 2 var))
(times_two '(+ 1 2))

在我的想象中,我认为扩张将是(* 2(+ 1 2))。并且在执行之后,结果将是6.但是失败了。

我不知道为什么。我阅读了Emacs lisp手册,但我仍然无法理解它们。我想知道在构建扩展时究竟是什么步骤。口译员做了什么?

2 个答案:

答案 0 :(得分:7)

当我在Emacs中评估这些表单时,在评估第二个表单时收到此错误消息:

Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p (quote (+ 1 2)))
  *(2 (quote (+ 1 2)))
  (lambda (var) (* 2 var))((quote (+ 1 2)))
  (times_two (quote (+ 1 2)))
  eval((times_two (quote (+ 1 2))))
  eval-last-sexp-1(nil)
  eval-last-sexp(nil)
  call-interactively(eval-last-sexp nil nil)

这是向您展示它如何扩展宏,它应该告诉您出了什么问题。 (最终扩展位于顶部。)

引用的表达式'(+ 1 2)将传递给times_two宏,但引用的列表不是*函数的有效参数。

你真正想要的是:

(defmacro times_two (var) `(* 2 ,var))
(times_two (+ 1 2))

请记住,通常,宏的结果将是新的Lisp代码,而不是最终值。编写宏的目的是构建能够为您提供所需结果的表单。因此,大多数时候你的宏最终会使用quasiquote(`)语法。

答案 1 :(得分:1)

我怀疑你混淆了编译时和运行时。宏在编译时运行,生成要在运行时执行的代码。一般来说,很难保持这些直线,这使得编写宏很困难。

无论如何,当我把它放入ielm时,我得到:

    ELISP> (defmacro times_two (var) 
       (* 2 var))
    times_two
    ELISP> (times_two '(+ 1 2))
    *** Eval error ***  Wrong type argument: number-or-marker-p, (quote (+ 1 2))
    ELISP> 

至少部分问题是'(+ 1 2),但是当我删除引用时,以下情况就更好了:

    ELISP> (times_two (+ 1 2))
    *** Eval error ***  Wrong type argument: number-or-marker-p, (+ 1 2)
    ELISP> 

似乎elisp正在寻找我们放置'(+ 1 2)和(+ 1 2)的地方的数字或标记。让我们尝试使用数字:

    ELISP> (times_two 3)
    6

有效。

有趣的是,这种宏观扩张给出了:

    ELISP> (macroexpand '(times_two 3))
    6

这可能不是我们想要的。

当我们编写宏时,我们希望返回在运行时要计算的表达式。因此,我们可以尝试这样做而不是返回一个数字:

    ELISP> (defmacro times_two (var) 
           `(* 2 ,var))

反引号(quasiquote)是一种创建列表的方法,但也允许使用逗号进行插值。以这种方式定义times_two给出:

    ELISP> (times_two (+ 1 2))
    6

扩展:

    ELISP> (macroexpand '(times_two (+ 1 2)))
    (* 2
       (+ 1 2))

这正是你想象的。