gpt4 book ai didi

macros - clojure defmacro 中的多重性

转载 作者:行者123 更新时间:2023-12-04 19:47:32 24 4
gpt4 key购买 nike

我在 Clojure 中遇到了一个与 defmacro 相关的奇怪问题,我有这样的代码

(defmacro ttt
([] (ttt 1))
([a] (ttt a 2))
([a b] (ttt a b 3))
([a b c] `(println ~a ~b ~c)))

我用(ttt)运行,它应该变成(println 1 2 3),并打印“1 2 3”,但我得到的是

ArityException Wrong number of args (-1) passed to: t1$ttt clojure.lang.Compiler.macroexpand1 (Compiler.java:6473)

经过一些调查,我明白我应该写

(defmacro ttt
([] `(ttt 1))
([a] `(ttt ~a 2))
([a b] `(ttt ~a ~b 3))
([a b c] `(println ~a ~b ~c)))

但为什么第一个版本失败了?而args太奇怪了,看不懂,-1是从哪里来的?

最佳答案

宏有两个隐藏参数

宏有两个隐藏参数 &form&env,它们提供有关调用和绑定(bind)的附加信息,这些信息是这里出现 arity 异常的原因。要在同一宏中引用其他 arity 版本,请使用准引号扩展。

(defmacro baz
([] `(baz 1))
([a] `(baz ~a 2))
([a b] `(baz ~a ~b 3))
([a b c] `(println ~a ~b ~c)))

user=> (macroexpand-1 '(baz))
(clojure.core/println 1 2 3)

user=> (baz)
1 2 3
nil

Arity 异常消息从计数中减去隐藏的参数

您获得 (-1) arity 异常的原因是因为编译器在为一般宏用法生成错误消息时减去这两个隐藏参数。您的第一个版本的 ttt 的真实消息是“参数数量错误 (1)”,因为您提供了一个参数 a 但未提供另外两个隐藏参数通过 self 调用。

在野外不常见的多参数宏

在实践中,我建议完全避免使用多重宏。相反,考虑一个辅助函数来代表宏完成大部分工作。实际上,这对于其他宏也是一种很好的做法。

(defn- bar
([] (bar 1))
([a] (bar a 2))
([a b] (bar a b 3))
([a b c] `(println ~a ~b ~c)))


(defmacro foo [& args] (apply bar args))

user=> (macroexpand-1 '(foo))
(clojure.core/println 1 2 3)

user=> (foo)
1 2 3
nil

宏展开是递归的

由于宏扩展的递归性质,您的第二个 ttt 版本也能正常工作

user=> (macroexpand-1 '(ttt))
(user/ttt 1)
user=> (macroexpand-1 *1)
(user/ttt 1 2)
user=> (macroexpand-1 *1)
(usr/ttt 1 2 3)
user=> (macroexpand-1 *1)
(clojure.core/println 1 2 3)

所以,

user=> (macroexpand '(ttt))
(clojure.core/println 1 2 3)

关于macros - clojure defmacro 中的多重性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37612568/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com