gpt4 book ai didi

macros - clojure 的 defmacro 中的多重参数

转载 作者:行者123 更新时间:2023-12-03 11:15:21 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 的真实信息在这里将是“错误数量的 args (1)”,因为您提供了一个参数 a但是两个额外的隐藏参数不是由自调用提供的。

多参数宏在野外并不常见

在实践中,我建议完全避免使用多参数宏。相反,考虑一个辅助函数来代表宏完成大部分工作。事实上,这通常也是其他宏的好习惯。
(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/25566146/

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