宏内调用函数和宏之间的区别? [英] Difference between calling function and macro inside macro?

查看:107
本文介绍了宏内调用函数和宏之间的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的拼图是以下示例:

 (defmacro macro1 [x] 
(println x))

(defn func1 [x]
(println x))

(defmacro macro2 [x]
`(macro1〜x)
(func1 x))

(defmacro macro3 [x]
(func1 x)
`(macro1〜x))

(println )
(macro2 hello)

(print1nmacro3)
(macro3 hello)
令人惊讶的是,输出是:

  macro2 
hello
macro3
hello
hello

为什么macro2和macro3的输出不同?在我的理解,所有宏宏内部的调用可以替换为函数(除了重用的原因)。我的理解错了什么?






感谢Michael澄清。我的一般问题是如何在使用函数或宏内宏中的操作s表达式的目的之间进行选择。我不知道它们是否可以可交换地使用,除非它们在不同阶段被评估。另一个示例:

 (defn manipulate-func [x] 
(list + x 1))

(defmacro manipulate-macro [x]
(list + x 1))

(defmacro macro1 [xy]
[操作宏宏)))

(println(clojure.walk / macroexpand-all'(macro1(+ 1 2)(+ 3 4))))
; [(#< core $ _PLUS_ clojure.core$_PLUS_@332b9f79>(+ 1 2)1)(#


解决方案

macro2 't call macro1 。看看它的身体:

 `(macro1〜x)
(func1 x)

第一行是语法引用的;它的值是(user / macro1 x-value)形式的列表结构(假设 macro1 code> user 命名空间; x-value 这里是提供给 macro2的文字参数),没有副作用。






响应编辑:

/ p>

首先,在宏主体内将调用另一个宏与发出调用区分到另一个宏是很重要的:

 (defmacro some-macro [] 
...)

;;调用some-macro:
(defmacro example-1 []
(some-macro))

;;发出对某些宏的调用:
(defmacro example-2 []
`(some-macro))

其次,在宏内部调用函数和宏的情况下,必须记住运行时和编译时间的相关概念:


$ b从宏扩展器的运行时调用宏调用的$ b

  • 函数,这是从用户代码的角度来看的编译时间;




p>如果宏发出对另一个宏的调用,与发出的宏调用相关的运行时和编译时间的概念将与那些与原始宏调用相关的概念相同。如果一个宏调用另一个宏,它们会向后移动一步。



为了说明,我们考虑一个宏,它将所有的工作委托给一个帮助函数:

 (defn emit-abc [abc-name [abc]] 
`(def〜abc-name { a〜a:b〜b:c〜c}))

(defmacro defabc [abc-name abc-vals]
(emit-abc abc-name abc-vals))

从REPL:

  user> (defabc foo [1 2 3])
#'user / foo
user> foo
{:a 1,:c 3,:b 2}

c $ c> emit-abc 本身就是一个宏,上面的定义 defabc 甚至不会编译,因为 emit-abc 将尝试重构字符 abc-vals ,引发 UnsupportedOperationException



这里有另一个例子,可以更容易地解释发生了什么:

 (let [[abc] [1 2 3]] 
(defabc foo [abc]))

defabc 接收三个文字符号的向量 a b c 作为第二个参数;它无法访问运行时值 1 2 3 。它将这个符号的精确向量传递给函数 emit-abc ,然后能够到达这个向量并提取符号以产生映射 {:aa:bb:cc} 。这个映射成为 defabc 调用的扩展。在运行时 a b c 绑定到 1 2 因此生成了 {:a 1:b 2:c 3}



假设我们试图写 emit-abc 作为具有相同主体的宏(只需将 defn 更改为 defmacro 在其定义)。然后,我们不能有用地从 defabc 中调用它,因为我们不会有任何方式传递给 defabc的参数的实际值。我们可以写

 (emit-abc abc-name [(abc-vals 0)(abc-vals 1)(abc- vals 2)])

以使 defabc 编译,但是这将最终发出 abc-name 作为被定义的Var的名称,并包括向量文字的代码 [abc] 三次在生成的代码。然而我们可以调用它:

 `(emit-abc〜abc-name〜abc-vals)

这样可以正常工作。


My puzzle is the following example:

(defmacro macro1 [x]
  (println x))

(defn func1 [x]
  (println x))

(defmacro macro2 [x]
  `(macro1 ~x)
  (func1 x))

(defmacro macro3 [x]
  (func1 x)
  `(macro1 ~x))

(println "macro2")
(macro2 hello)

(println "macro3")
(macro3 hello)

Surprisingly, the output is:

macro2
hello
macro3
hello
hello

Why the output of macro2 and macro3 are different? In my understanding, all the calling of macro inside macro could be substituted with function (except for the reason of reuse). Anything wrong in my understanding?


Thanks Michael for clarifying. My general question is how to choose between using function or macro inside macro for the purpose of manipulating the s-expression. I wonder whether they can be used exchangeably except that they're evaled at different phases. Another example:

(defn manipulate-func [x]
  (list + x 1))

(defmacro manipulate-macro [x]
  (list + x 1))

(defmacro macro1 [x y]
  [(manipulate-func x) `(manipulate-macro ~y)])

(println (clojure.walk/macroexpand-all '(macro1 (+ 1 2) (+ 3 4))))
;; [(#<core$_PLUS_ clojure.core$_PLUS_@332b9f79> (+ 1 2) 1) (#<core$_PLUS_ clojure.core$_PLUS_@332b9f79> (+ 3 4) 1)]

解决方案

macro2 doesn't call macro1. Look at its body:

`(macro1 ~x)
(func1 x)

The first line is syntax-quoted; its value is list structure of the form (user/macro1 x-value) (assuming macro1 is defined in the user namespace; x-value here is the literal argument provided to macro2) and it has no side effects. Because there are no side effects and the value is discarded, this line has no effect.


Responding to the edit:

Firstly, it is important to distinguish calling another macro inside a macros body from emitting a call to another macro:

(defmacro some-macro []
  ...)

;; calls some-macro:
(defmacro example-1 []
  (some-macro))

;; emits a call to some-macro:
(defmacro example-2 []
  `(some-macro))

Secondly, in the case of calling functions and macros inside a macro's body, one must keep in mind what the relevant notions of runtime and compile time are:

  • functions called by a macro will be called at the macro expander's runtime, which is compile time from the point of view of user code;

  • macros called by a macro will be expanded when the macro body is compiled.

If a macro emits a call to another macro, the notions of runtime and compile time relevant to the emitted macro call will be the same as those relevant to the original macro call. If a macro calls another macro, they are shifted one step back, as it were.

To illustrate, let's consider a macro that delegates all its work to a helper function:

(defn emit-abc [abc-name [a b c]]
  `(def ~abc-name {:a ~a :b ~b :c ~c}))

(defmacro defabc [abc-name abc-vals]
  (emit-abc abc-name abc-vals))

From the REPL:

user> (defabc foo [1 2 3])
#'user/foo
user> foo
{:a 1, :c 3, :b 2}

If emit-abc were itself a macro, the above definition of defabc wouldn't even compile, because emit-abc would attempt to destructure the literal symbol abc-vals, throwing an UnsupportedOperationException.

Here's another example that makes it easier to explain what's happening:

(let [[a b c] [1 2 3]]
  (defabc foo [a b c]))

defabc receives the vector of the three literal symbols a, b and c as the second argument; it has no access to the runtime values 1, 2 and 3. It passes this exact vector of symbols to the function emit-abc, which is then able to reach into this vector and extract the symbols to produce the map {:a a :b b :c c}. This map becomes the expansion of the defabc call. At runtime a, b and c turn out to be bound to the values 1, 2 and three, and so the map {:a 1 :b 2 :c 3} is produced.

Suppose we tried to write emit-abc as a macro with the same body (just changing defn to defmacro in its definition). Then we couldn't usefully call it from defabc, because we wouldn't have any way of conveying to it the actual values of the arguments to defabc. We could write

(emit-abc abc-name [(abc-vals 0) (abc-vals 1) (abc-vals 2)])

to make defabc compile, but this would end up emitting abc-name as the name of the Var being defined and include code for the vector literal [a b c] three times in the generated code. We could however emit a call to it:

`(emit-abc ~abc-name ~abc-vals)

This works as expected.

这篇关于宏内调用函数和宏之间的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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