深入了解lisp(clojure)-宏

来源:互联网 发布:贝伦赛丽40周年 知乎 编辑:程序博客网 时间:2024/05/16 07:30

本文由larrylgq编写,转载请注明出处:http://blog.csdn.net/larrylgq/article/details/7395261

作者:吕桂强

邮箱:larry.lv.word@gmail.com



lisp使用特殊操作符来扩展语法,但是在lisp中特殊操作符的数量是固定的,为了解决这个问题lisp引入了宏,宏并不直接做事,而是生成实际的业务代码。

宏的求值过程:接收S-表达式为参数,返回一个lisp展开式(ecpansion),对该展开式求值。

编译过程:在clojure中使用compile编译源文件的时候,将文件中所有宏递归展开成展开式,此时代码中只有函数调用形式和特殊形式。将展开后的代码编译成FASL文件,使用LOAD函数加载时会执行这些编译过的代码。

由于宏不会被直接求值的特性,所以我们定义的宏可以不是标准格式的lisp,每个宏都可以定义自己的语法。

相较于使用核心加上标准库的方式定义的语言,lisp可以通过宏来定义新的语法,并将它放在标准库里,而不是硬编码在语言核心。

当然你也可以将自己会大量复用的代码抽象成宏来使代码更简洁。

 

宏的结构:名字+形参列表+可选文档字符串+声明+lisp表达式体

一个简单的例子,使用宏实现

(defmacro while  "Repeatedly executes body while test expression is true. Presumes  some side-effect will cause test to become false/nil. Returns nil"  {:added "1.0"}  [test & body]  `(loop []     (when ~test       ~@body       (recur))))


当我们这样调用的时候:

user=> (def a (atom 10))                                #'user/auser=> (while (pos? @a) (do (println @a) (swap! a dec)))

将会在编译器产生如下代码:

(defn while [(pos? a) (do (println @a) (swap! a dec))]

(loop [] (

 (when (pos? a)

  (do (println @a) (swap! a dec))

  (recur)

)

)))

符号使用:

反引号 :防止宏体内的表达式被evaluate。

波浪号:使用在在反引号里面,它的值会被替换。

~@: 替换某个具体元素。