【问题标题】:Negative arity exception in recursive macro递归宏中的负数异常
【发布时间】:2017-02-16 13:22:13
【问题描述】:

我正在尝试一个coding challenge,它要求您创建可以无限编译的代码。

我的第一个想法是一个永远扩展为自身的宏。我写了:

(defmacro a []
    (a))
(a)

这实际上并没有产生任何东西,但我希望它永远循环。然而,相反,我得到了一个荒谬的arity异常:

Wrong number of args (-2) passed to: infinite-compile/a, compiling:...

如果我尝试给它一个 kicks 参数,它现在会抱怨宏不期望任何参数。

如果我让它实际产生对自身的调用:

(defmacro a []
    `(a))
(a)

它因 StackOverflow 而失败,这是我预期的。

这里发生了什么?为什么它认为我正在传递宏“-2”参数?我能想到的唯一可能的事情是它与传递给宏的 2 个隐式 & 参数有关,但这只是在黑暗中的一个镜头,并没有真正解释发生了什么。

为了解决这个问题,这似乎不是多元宏的问题,因为它只有一个 0 元版本。此外,显式传递隐式参数不会做任何事情:

(defmacro a []
    (a &form &env))

这会产生:

Compiler Exception clojure.lang.ArityException: Wrong number of args (2) passed to: infinite-compile/a, compiling:

【问题讨论】:

    标签: clojure macros arity


    【解决方案1】:

    您期望(a)a 的定义期间被宏扩展,而a 还不知道是一个宏(因此应该是一个函数),而当您引用表单时,您正在有效地构建一个扩展为对自身的调用的宏。

    当编译器宏展开宏时,它会在调用与宏关联的函数之前添加隐式参数:Compiler.java#L6795。在这里,您直接调用a,它在隐式defn (core.clj#L452) 的范围内,没有传递必要的参数。

    我希望以下内容可以按您的意愿工作(循环):

    user=> (defmacro a[]&form)
    #'user/a
    user=> (a)
    

    但不幸的是,这是我收到的错误消息:

    CompilerException java.lang.RuntimeException: Can't take value of a macro: #'user/a, compiling:(/tmp/form-init5239882861501900074.clj:1:1) 
    

    ...即使:

    user=> (defmacro a[](print &form))
    #'user/a
    user=> (a)
    (a)nil
    

    注意。 Common Lisp 等价物是:

    (defmacro w(&whole w)w) ;; 23 bytes
    

    现有名称

    另请注意,一旦您定义了a,您将无法按如下方式更改定义:

    (defmacro a[](a 0 1))
    

    ... 因为它抱怨 a 接受零参数。但是,如果您使用尚未定义的名称定义 另一个 宏,它会起作用:

    user=> (defmacro b[](b 0 1))
    #'user/b
    user=> (b)
    
    StackOverflowError   user/b (form-init5239882861501900074.clj:1)
    

    【讨论】:

    • 仍然有点困惑,因为我显然不太了解宏是如何实现的,但这通常是有道理的。谢谢。
    • 宏只接受代码并发出代码(另见github.com/clojure/clojure/blob/…)。但是您发现了一些极端情况,其行为不一定直观也不明智。
    • 我理解那部分。您提到了“隐式定义”。 defmacro 会发出 defn 吗?也许我会再看一遍defmacro的来源。
    • @Carcigenicate (list 'do 'defn ...) 部分构建定义函数并声明名称绑定到宏的代码。例如,该代码可能会被编译器评估(未检查)。
    【解决方案2】:

    有关问题的更多描述,请参阅http://dev.clojure.org/jira/browse/CLJ-1279 的票证。

    【讨论】:

    • 这不是 Stack Overflow 标准的答案。作为对问题或现有答案的评论,或者如果您在此处另外总结了该链接中的重要内容,这将很有帮助。
    猜你喜欢
    • 2013-11-15
    • 2018-01-03
    • 2012-10-25
    • 2012-07-25
    • 2022-01-23
    • 1970-01-01
    • 2018-02-05
    • 2013-11-21
    • 2015-02-20
    相关资源
    最近更新 更多