2

我正在尝试一个编码挑战,要求您创建无限编译的代码。

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

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

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

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

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

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

(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:
4

2 回答 2

2

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

当编译器宏展开宏时,它会在调用与宏关联的函数之前添加隐式参数:Compiler.java#L6795。在这里,您直接调用a,它在隐式defncore.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)
于 2017-02-16T13:52:03.383 回答
-1

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

于 2017-02-16T15:02:41.893 回答