码迷,mamicode.com
首页 > 编程语言 > 详细

Clojure语言十三: 宏

时间:2015-10-18 23:20:05      阅读:429      评论:0      收藏:0      [点我收藏+]

标签:clojure

micro的核心作用

由于micro接收的参数可以很复杂,而且不求值,因此可以接收list,在内部转换,加工后执执行,也可以接收完全不是lisp的语法,在内部转换成clojure能执行的list。

这样可以创建自己的DSL,而把clojure宏实现为DSL解析器。

micro与function的区别

特点是允许在Clojure evaluate你的list之前,你能够像处理函数一样的处理它, 但是不evaluate。

如果你想把一个表达式作为参数传递给函数,并要求这个参数不求值,这是做不到的。比如下面的函数定义会报错:

user=> (def ignore-last-operand2
  [function-call]
  (butlast function-call))
  #_=>   #_=> 
user=> CompilerException java.lang.RuntimeException: Too many arguments to def, compiling:(/tmp/form-init3734404425586418360.clj:1:1) 


本意是ignore-last-operand2接收一个list参数,但是不求值,在body运行的时候才进行求值,而且要忽略list的最后一个symbol.


这种情况需要使用宏了。

user=> (defmacro ignore-last-operand
  [function-call]
  (butlast function-call))
  #_=>   #_=> #‘user/ignore-last-operand
user=> (ignore-last-operand (+ 1 2 10))
3


和C的宏有点像,不过C的宏是在预编译阶段做文本替换,还是弱了点。不像lisp可以在运行时随意使用宏。

用macroexpand检查返回的list

上面的例子中,其实经过了去除最后一个symbol,然后再对余下的list求值的过程。

(+ 1 2 10) -> (+ 1 2) -> 3

要想知道最后求值结果出来前的list,可以使用macroexpand. 注意要配合使用‘ 表示不要求值。

user=> (macroexpand ‘(ignore-last-operand (+ 1 2 10)))
(+ 1 2)

这样就可以显示宏返回的list,而不是求值。


常用宏


-> 宏改变从右到左的阅读方式

(defn read-resource
  "Read a resource into a string"
  [path]
  (read-string (slurp (clojure.java.io/resource path))))

这个函数运算的时候会先从最右边的(因为也是最里面)的list开始求值,path -> (clojure...) -> (slurp...) -> (read-string ...), 然后往外层层求值。通过->宏可以将最先求值的放在最上面,写成这样。

(defn read-resource
  [path]
  (-> path
      clojure.java.io/resource
      slurp
      read-string))

每个人有不同的偏好,有些人还是会这样用,为了减少括号的数目和符合自己从左到右或者从上到下的阅读习惯。


binding宏

binding宏用来创建线程专属的变量,在多线程中很常用。

user=> (def ^:dynamic x 1)
user=> (def ^:dynamic y 1)
user=> (+ x y)
2
 
user=> (binding [x 2 y 3]
         (+ x y))
5
 
user=> (+ x y)
2








版权声明:本文为博主原创文章,未经博主允许不得转载。

Clojure语言十三: 宏

标签:clojure

原文地址:http://blog.csdn.net/csfreebird/article/details/49230131

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!