Clojure 学习入门(11)- 宏 macro

来源:互联网 发布:天刀女捏脸数据导入 编辑:程序博客网 时间:2024/04/30 07:17
clojure macro宏在运行之前机械展开,定义宏相当于给语言增加新特性,写宏的*原则*: 

  • 能写成函数就不要用宏(因为写宏没有写函数简单直观,容易写错,需要先在 REPL 中测试一番) 
  • 只有不得不用时才用宏(性能要求高时比函数调用快,或者需要“代码<->数据”相互转换) 
  • 精心设计的宏调用比函数调用更 DSL(如实现控制结构、传递 Java方法) 


宏与函数

[python] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. ;; 宏正确写法  
  2. (defmacro op [x f1 y f2 z]  
  3.   (list f2 z (list f1 x y)))        
  4.   
  5. (println (op 5 + 2 * 10))                   ;; 70  
  6. (println (macroexpand '(op 5 + 2 * 10)))                     ;; (* 10 (+ 5 2))  
  7. (println (macroexpand-1 '(op 5 + 2 * 10)))                   ;; (* 10 (+ 5 2))  
  8. (println (clojure.walk/macroexpand-all '(op 5 + 2 * 10)))    ;; (* 10 (+ 5 2))  
  9.   
  10. ;; 宏错误写法  
  11. (defmacro op2 [x f1 y f2 z]  
  12.   ( f2 z (f1 x y)))  
  13.   
  14. (println (op2 5 + 2 * 10))                  ;; 2  
  15. (println (macroexpand '(op2 5 + 2 * 10)))                    ;; 2  
  16. (println (macroexpand-1 '(op2 5 + 2 * 10)))                  ;; 2  
  17. (println (clojure.walk/macroexpand-all '(op2 5 + 2 * 10)))   ;; 2  
  18.   
  19. ;; 不使用宏  
  20. (defn op3 [x f1 y f2 z]  
  21.   ( f2 z (f1 x y)))  
  22.   
  23. (println (op3 5 + 2 * 10))                  ;; 70  
  24. (println (macroexpand '(op3 5 + 2 * 10)))                    ;; (op3 5 + 2 * 10)  
  25. (println (macroexpand-1 '(op3 5 + 2 * 10)))                  ;; (op3 5 + 2 * 10)  
  26. (println (clojure.walk/macroexpand-all '(op3 5 + 2 * 10)))   ;; (op3 5 + 2 * 10)  

说明: 

正确的宏写法,需要添加 list,宏用defmacro定义,不用宏写法的函数用defn定义
调试宏,用macroexpand展开


宏符号

`

原原本本地直译过去,不用`,let语句不被翻译,例如: (let [datastr '{:a 1 :b 2}])

~'

后面的变量被直接翻译过去,例如:(let [~'conn "meta"] (with-mongo ~'conn))

'~

变量名本身而非值,例如:(defn f1 [x] (println '~x ":" ~x))  (let [a 10] (f1 a)) ;; a:10

~@

表示多条语句


示例1:

[python] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. (defmacro debug [x] `(println "---" '~x ":" ~x))  
  2. (let [a 10] (debug a))             ;; --- a : 10  
说明:

'~x  显示变量名,即a

~x 解析为变量值,即a的值 10


示例2:

[python] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. (defn make-connection [x] (println "in make-connection = " x) x)    ;; meta  
  2. (defn with-mongo [x] (println "in with-mongo = " x))   ;; meta  
  3.   
  4. (defmacro with-dict  
  5.   "连接到 meta库的 dict表进行操作"  
  6.   [& body]  
  7.   `(let [~'dbname "meta"  
  8.          ~'tbname :dict  
  9.          ~'conn (make-connection ~'dbname)  
  10.          ]  
  11.      (with-mongo ~'conn)  
  12.      (println "~'conn = " ~'conn)   ;; meta  
  13.      (println "~'tbname = " ~'tbname)   ;; :dict  
  14.      (println "~@body = " ~@body)       ;; meta :dict db-test2' tbl-test2'  
  15.      ~@body))  
  16.   
  17.   
  18. (let [dbname 'db-test'   
  19.       tbname 'tbl-test'  
  20.       dbname2 'db-test2'   
  21.       tbname2 'tbl-test2'  
  22.       conn 'conn-sql'  
  23.       make-connection 'make-conn'  
  24.       body 'body1']  
  25.   (with-dict dbname tbname dbname2 tbname2))  
运行结果:

in make-connection =  meta
in with-mongo =  meta
~'conn =  meta
~'tbname =  :dict
~@body =  meta :dict db-test2' tbl-test2'

说明:

make-connection 和 with-mongo 是定义的函数,后面传递的是参数,使用 ~' 修饰直接翻译过去,即字符串传字符串,:dict 键值也传键值

with-dict 传递多个参数给body,其中 dbname tbname 在 with-dict 中被重新赋值,因此打印出的结果也为赋值后的最新结果

0 0