【问题标题】:Unbound variable error when using defmacro in LISP在 LISP 中使用 defmacro 时出现未绑定变量错误
【发布时间】:2017-03-28 12:26:51
【问题描述】:

我正在尝试在 LISP 中使用具有两种形式的宏,它评估两种形式但总是返回形式 2 的结果。下面是我正在使用的代码 -

(defmacro testmac (x body) (prog2 x body))

当使用以下形式执行宏时,它可以正常工作,并且总是返回 5,即第二种形式。

(testmac (- 10 6) (/ 10 2))

但是,当我尝试使用以下形式执行宏时,它会返回错误。

(testmac (print a) (print b)) 

以下是我得到的错误 -

debugger invoked on a UNBOUND-VARIABLE: The variable B is unbound.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-INT:SIMPLE-EVAL-IN-LEXENV B #<NULL-LEXENV>)

为什么会出现这个错误,我该如何使用宏来完成这项工作?

附:我不能用defun 需要用宏来执行(testmac (print a) (print b))

【问题讨论】:

  • 欢迎使用 StackOverflow!这是一个结构良好的第一个问题。但是......请写“Lisp”,不带大写;)
  • @Ealhad。当然,感谢您的建议。
  • 不客气!祝你好运,黑客愉快。
  • @Rainer @ Ealhad,感谢大家的反馈和建议。根据你们所指出的,我正在执行代码为 (testmac (print 'a) (print 'b)) ,它返回 B 并且没有错误。它解决了我试图解决的原始问题,即使用宏和 prog2 来评估两种形式(@rainer,我知道这是不可取的)并返回第二种形式的结果。我希望这是正确的解决方案,而不是解决方法。谢谢。

标签: lisp common-lisp clisp


【解决方案1】:

我正在尝试在 LISP 中使用具有两种形式的宏,它会评估两种形式,但总是返回形式 2 的结果。

这通常不是一个好主意——尽管它可能只是措辞不准确。宏不应评估代码 - 并非没有充分的理由。通常宏只是转换代码。然后生成的代码决定评估什么。

(defmacro testmac (x body) (prog2 x body))

(testmac (- 10 6) (/ 10 2))

所以x 是列表(- 10 6) 而body 是列表(/ 10 2)

您的宏返回第二个列表。

CL-USER 11 > (macroexpand-1 '(testmac (print a) (print b)))
(PRINT B)

宏返回(print b) 的形式。然后它会被执行。

CL-USER 12 > (testmac (print a) (print b))

Error: The variable B is unbound.

如果 B 未定义,则会收到您看到的错误。

没有魔法发生。

【讨论】:

    【解决方案2】:

    那是因为你的testmac 宏直接执行(prog2 x body),而不是扩展到它。

    你需要这样写:

    (defmacro testmac (x body)
      `(prog2 ,x ,body))
    

    不计算反引号后的形式,但逗号后的形式会。

    您可以(并且应该!)像这样测试扩展:

    (macroexpand-1 '(testmac (print 42) (print 51)))
    

    这给出了:

    (PROG2 (PRINT 42) (PRINT 51))
    

    【讨论】:

    • 感谢您的意见。 1. (PROG2 (PRINT 42) (PRINT 51)) 和 testmac (print 42 和 print 51) 都正确返回结果。但是,当我尝试执行 (prog2 (print a) (print b)) 时,它不再工作并返回错误 - 在 UNBOUND-VARIABLE 上调用的调试器:变量 PROG2 未绑定
    • 呃,这很奇怪。我发现重现此错误的唯一方法是使用prog2 (print a) (print b),没有外部括号,这是有充分理由的。
    • @MikeRandor 明确一点,当您将 testmac 替换为 progn 时,ei。 (progn (print a) (print b)) ,您没有收到错误消息吗?
    • @Sylwester,根据 Rainer 所指出的, (progn (print a) (print b)) 将返回并导致错误,因为 progn/ prog2 正在评估 a 和 b,它们是不明确的。我试图通过执行 (testmac (print 'a) (print 'b)) 来解决这个问题,它可以正常工作并打印“b”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-04
    • 2019-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多