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



我试着写一个宏,并按如下方式执行。但未能执行。

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

在我的想象中,我认为膨胀应该是(*2(+12))。并且在执行之后,结果将是6。但失败了。

我不知道为什么。我读了Emacs的lisp手册,但还是听不懂。我想知道扩建工程的具体步骤是什么。翻译做了什么?

当我在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代码,而不是最终值。编写宏的目的是构建一个能给你想要的结果的表单。因此,在大多数情况下,宏最终将使用准引号(`)语法。

我怀疑您混淆了编译时和运行时。宏在编译时运行,生成要在运行时执行的代码。一般来说,很难保持这些内容的正确性,这使得编写宏变得困难。

无论如何,当我把这个放进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))

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

    ELISP> (times_two (+ 1 2))
    6

和扩展:

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

这正是你想象的。

最新更新