clojure的宏

1, defmacro和defn的区别

defn defines a function, defmacro defines a macro.

The difference between functions and macros is that on a function call first the arguments of the function are evaluated then the body of the function is evaluated using the arguments.

Macros on the other hand describe a transformation from one piece of code to another. Any evaluation takes place after the transformation.

This means that arguments may be evaluated multiple times or not at all. As an example or is a macro. If the first argument of or is false, the second argument will never be evaluated. If or were a function, this would not be possible, because the arguments would always be evaluated before the function runs.

Another consequence of this is that the arguments of a macro need not be a valid expression before the macro is expanded. For example you could define a macro mymacro such that (mymacro (12 23 +)) expands to (+ 23 12), so this will work even though (12 23 +) on its own would be nonsense. You couldn't do that with a function because (12 23 +) would be evaluated, and cause an error, before the function runs.

A small example to illustrate the difference:

(defmacro twice [e] `(do ~e ~e))
(twice (println "foo"))

The macro twice gets the list (println "foo") as an argument. It then transforms it into the list (do (println "foo") (println "foo")). This new code is what gets executed.

(defn twice [e] `(do ~e ~e))
(twice (println "foo"))

Here println "foo" is evaluted right away. Since println returns nil, twice is called with nil as its argument. twice now produces the list (do nil nil) and returns that as its result. Note that here (do nil nil) is not evaluated as code, it's just treated as a list.

附链接地址:
What is the difference between defn and defmacro?

核心区别在于,方法调用本身是值传递,所以在调用defn的逻辑时,入参都会evaluated的,但是调用defmacro的逻辑的时候却不会,宏在展开求值之前是不会对参数做evaluated的。

示例:

(defn test5 [x y z]
  `(let [~z (+ ~x ~y)]
    (prn ~z)))
;(test5 1 2 :z)

(defmacro test6 [x y z]
  `(let [~z (+ ~x ~y)]
     (prn ~z)))

;;;defn 和defmacro的本质区别?为什么defmacro接收的变量不会立即求值?
(defn -main []
  ;(test5 1 2 z)) ;;会报错,调用的时候就会对z进行求值处理
  (test6 1 2 z)) ;;不会报错

单看defmacro的实现只是def了一个匿名函数,为什么这个匿名函数接收的变量可以不用立马求值呢?

2,对Syntax Quote的理解

;;(test1 1 2)报错
(defmacro test1 [x y]
  `(prn (+ x y)))

Syntax Quote的作用是将对应list里面的内容都变成symbol,prn+等symbol均已内置定义。
例如:

`+
=> clojure.core/+

`prn
=> clojure.core/prn

但是x和y并没有定义,所以执行的时候会报错。对于这些未定义的symbol,可以通过插值处理来解决这个问题。

;;(test1 1 2)正常运行
(defmacro test1 [x y]
  `(prn (+ ~x ~y)))

3,gensym (prefix# )的作用

gensym文档
示例:

(defmacro dbg [& body]
  `(let [x# ~@body]
     (println (str "dbg: " (quote ~@body) "=" x#))
     x#))
user> (macroexpand-1 '(dbg (+ 1 2)))
(let [x__2071__auto__ (+ 1 2)]
  (println (str "dbg: " (quote (+ 1 2)) "=" x__2071__auto__))
  x__2071__auto__)

如果不使用gensym定义x#,而是直接绑定一个变量,那么该symbol在当前namespace中是找不到的,会报错的,所以通过该方式动态生成一个symbol用于接收绑定的值。
dbg: A Cool Little Clojure Macro

posted @ 2022-10-18 20:27  堕落门徒  阅读(34)  评论(0编辑  收藏  举报