clojure 的 defmacro 中的多个参数 [英] multiple arity in defmacro of clojure

查看:16
本文介绍了clojure 的 defmacro 中的多个参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I encountered a strange problem relating to defmacro in Clojure, I have code like

(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",但我得到的是

and I run with (ttt), it suppose to become (println 1 2 3), and print "1 2 3", but what I got is

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

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

after some investigation, I understand I should write

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

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

but why the first version failed? and args is too strange to understand, where -1 comes from?

推荐答案

宏有两个隐藏参数

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

Macros have two hidden arguments

Macros have two hidden arguments &form and &env that provide additional information about invocation and bindings that are the cause of the arity exception here. To refer to other arity versions within the same macro, use quasi-quote expansion.

(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 但未提供两个额外的隐藏参数通过自我调用.

Arity exception messages subtract the hidden arguments from the count

The reason you get the (-1) arity exception is because the compiler subtracts these two hidden arguments when generating the error message for the general macro usage. The true message here for your first version of ttt would be "Wrong number of args (1)" because you supplied one argument a but the two additional hidden arguments were not provided by self-invocation.

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

In practice, I suggest avoiding multi-arity macros altogether. Instead, consider a helper function to do most of the work on behalf of the macro. Indeed, this is often a good practice for other macros as well.

(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 版本也能正常工作,因为宏扩展的递归性质

Macro expansion is recursive

Your second ttt version works as well due to the recursive nature of macro-expansion

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)

这篇关于clojure 的 defmacro 中的多个参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆